unrealon 1.0.9__py3-none-any.whl → 1.1.1__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.
- unrealon/__init__.py +23 -21
- unrealon-1.1.1.dist-info/METADATA +722 -0
- unrealon-1.1.1.dist-info/RECORD +82 -0
- {unrealon-1.0.9.dist-info → unrealon-1.1.1.dist-info}/WHEEL +1 -1
- unrealon-1.1.1.dist-info/entry_points.txt +9 -0
- {unrealon-1.0.9.dist-info → unrealon-1.1.1.dist-info/licenses}/LICENSE +1 -1
- unrealon_bridge/__init__.py +114 -0
- unrealon_bridge/cli.py +316 -0
- unrealon_bridge/client/__init__.py +93 -0
- unrealon_bridge/client/base.py +78 -0
- unrealon_bridge/client/commands.py +89 -0
- unrealon_bridge/client/connection.py +90 -0
- unrealon_bridge/client/events.py +65 -0
- unrealon_bridge/client/health.py +38 -0
- unrealon_bridge/client/html_parser.py +146 -0
- unrealon_bridge/client/logging.py +139 -0
- unrealon_bridge/client/proxy.py +70 -0
- unrealon_bridge/client/scheduler.py +450 -0
- unrealon_bridge/client/session.py +70 -0
- unrealon_bridge/configs/__init__.py +14 -0
- unrealon_bridge/configs/bridge_config.py +212 -0
- unrealon_bridge/configs/bridge_config.yaml +39 -0
- unrealon_bridge/models/__init__.py +138 -0
- unrealon_bridge/models/base.py +28 -0
- unrealon_bridge/models/command.py +41 -0
- unrealon_bridge/models/events.py +40 -0
- unrealon_bridge/models/html_parser.py +79 -0
- unrealon_bridge/models/logging.py +55 -0
- unrealon_bridge/models/parser.py +63 -0
- unrealon_bridge/models/proxy.py +41 -0
- unrealon_bridge/models/requests.py +95 -0
- unrealon_bridge/models/responses.py +88 -0
- unrealon_bridge/models/scheduler.py +592 -0
- unrealon_bridge/models/session.py +28 -0
- unrealon_bridge/server/__init__.py +91 -0
- unrealon_bridge/server/base.py +171 -0
- unrealon_bridge/server/handlers/__init__.py +23 -0
- unrealon_bridge/server/handlers/command.py +110 -0
- unrealon_bridge/server/handlers/html_parser.py +139 -0
- unrealon_bridge/server/handlers/logging.py +95 -0
- unrealon_bridge/server/handlers/parser.py +95 -0
- unrealon_bridge/server/handlers/proxy.py +75 -0
- unrealon_bridge/server/handlers/scheduler.py +545 -0
- unrealon_bridge/server/handlers/session.py +66 -0
- unrealon_browser/__init__.py +61 -18
- unrealon_browser/{src/cli → cli}/browser_cli.py +6 -13
- unrealon_browser/{src/cli → cli}/cookies_cli.py +5 -1
- unrealon_browser/{src/core → core}/browser_manager.py +2 -2
- unrealon_browser/{src/managers → managers}/captcha.py +1 -1
- unrealon_browser/{src/managers → managers}/cookies.py +1 -1
- unrealon_browser/managers/logger_bridge.py +231 -0
- unrealon_browser/{src/managers → managers}/profile.py +1 -1
- unrealon_driver/__init__.py +73 -19
- unrealon_driver/browser/__init__.py +8 -0
- unrealon_driver/browser/config.py +74 -0
- unrealon_driver/browser/manager.py +416 -0
- unrealon_driver/exceptions.py +28 -0
- unrealon_driver/parser/__init__.py +55 -0
- unrealon_driver/parser/cli_manager.py +141 -0
- unrealon_driver/parser/daemon_manager.py +227 -0
- unrealon_driver/parser/managers/__init__.py +46 -0
- unrealon_driver/parser/managers/browser.py +51 -0
- unrealon_driver/parser/managers/config.py +281 -0
- unrealon_driver/parser/managers/error.py +412 -0
- unrealon_driver/parser/managers/html.py +732 -0
- unrealon_driver/parser/managers/logging.py +609 -0
- unrealon_driver/parser/managers/result.py +321 -0
- unrealon_driver/parser/parser_manager.py +628 -0
- unrealon/sdk_config.py +0 -88
- unrealon-1.0.9.dist-info/METADATA +0 -810
- unrealon-1.0.9.dist-info/RECORD +0 -246
- unrealon_browser/pyproject.toml +0 -182
- unrealon_browser/src/__init__.py +0 -62
- unrealon_browser/src/managers/logger_bridge.py +0 -395
- unrealon_driver/README.md +0 -204
- unrealon_driver/pyproject.toml +0 -187
- unrealon_driver/src/__init__.py +0 -90
- unrealon_driver/src/cli/__init__.py +0 -10
- unrealon_driver/src/cli/main.py +0 -66
- unrealon_driver/src/cli/simple.py +0 -510
- unrealon_driver/src/config/__init__.py +0 -11
- unrealon_driver/src/config/auto_config.py +0 -478
- unrealon_driver/src/core/__init__.py +0 -18
- unrealon_driver/src/core/exceptions.py +0 -289
- unrealon_driver/src/core/parser.py +0 -638
- unrealon_driver/src/dto/__init__.py +0 -66
- unrealon_driver/src/dto/cli.py +0 -119
- unrealon_driver/src/dto/config.py +0 -18
- unrealon_driver/src/dto/events.py +0 -237
- unrealon_driver/src/dto/execution.py +0 -313
- unrealon_driver/src/dto/services.py +0 -311
- unrealon_driver/src/execution/__init__.py +0 -23
- unrealon_driver/src/execution/daemon_mode.py +0 -317
- unrealon_driver/src/execution/interactive_mode.py +0 -88
- unrealon_driver/src/execution/modes.py +0 -45
- unrealon_driver/src/execution/scheduled_mode.py +0 -209
- unrealon_driver/src/execution/test_mode.py +0 -250
- unrealon_driver/src/logging/__init__.py +0 -24
- unrealon_driver/src/logging/driver_logger.py +0 -512
- unrealon_driver/src/services/__init__.py +0 -24
- unrealon_driver/src/services/browser_service.py +0 -726
- unrealon_driver/src/services/llm/__init__.py +0 -15
- unrealon_driver/src/services/llm/browser_llm_service.py +0 -363
- unrealon_driver/src/services/llm/llm.py +0 -195
- unrealon_driver/src/services/logger_service.py +0 -232
- unrealon_driver/src/services/metrics_service.py +0 -185
- unrealon_driver/src/services/scheduler_service.py +0 -489
- unrealon_driver/src/services/websocket_service.py +0 -362
- unrealon_driver/src/utils/__init__.py +0 -16
- unrealon_driver/src/utils/service_factory.py +0 -317
- unrealon_driver/src/utils/time_formatter.py +0 -338
- unrealon_llm/README.md +0 -44
- unrealon_llm/__init__.py +0 -26
- unrealon_llm/pyproject.toml +0 -154
- unrealon_llm/src/__init__.py +0 -228
- unrealon_llm/src/cli/__init__.py +0 -0
- unrealon_llm/src/core/__init__.py +0 -11
- unrealon_llm/src/core/smart_client.py +0 -438
- unrealon_llm/src/dto/__init__.py +0 -155
- unrealon_llm/src/dto/models/__init__.py +0 -0
- unrealon_llm/src/dto/models/config.py +0 -343
- unrealon_llm/src/dto/models/core.py +0 -328
- unrealon_llm/src/dto/models/enums.py +0 -123
- unrealon_llm/src/dto/models/html_analysis.py +0 -345
- unrealon_llm/src/dto/models/statistics.py +0 -473
- unrealon_llm/src/dto/models/translation.py +0 -383
- unrealon_llm/src/dto/models/type_conversion.py +0 -462
- unrealon_llm/src/dto/schemas/__init__.py +0 -0
- unrealon_llm/src/exceptions.py +0 -392
- unrealon_llm/src/llm_config/__init__.py +0 -20
- unrealon_llm/src/llm_config/logging_config.py +0 -178
- unrealon_llm/src/llm_logging/__init__.py +0 -42
- unrealon_llm/src/llm_logging/llm_events.py +0 -107
- unrealon_llm/src/llm_logging/llm_logger.py +0 -466
- unrealon_llm/src/managers/__init__.py +0 -15
- unrealon_llm/src/managers/cache_manager.py +0 -67
- unrealon_llm/src/managers/cost_manager.py +0 -107
- unrealon_llm/src/managers/request_manager.py +0 -298
- unrealon_llm/src/modules/__init__.py +0 -0
- unrealon_llm/src/modules/html_processor/__init__.py +0 -25
- unrealon_llm/src/modules/html_processor/base_processor.py +0 -415
- unrealon_llm/src/modules/html_processor/details_processor.py +0 -85
- unrealon_llm/src/modules/html_processor/listing_processor.py +0 -91
- unrealon_llm/src/modules/html_processor/models/__init__.py +0 -20
- unrealon_llm/src/modules/html_processor/models/processing_models.py +0 -40
- unrealon_llm/src/modules/html_processor/models/universal_model.py +0 -56
- unrealon_llm/src/modules/html_processor/processor.py +0 -102
- unrealon_llm/src/modules/llm/__init__.py +0 -0
- unrealon_llm/src/modules/translator/__init__.py +0 -0
- unrealon_llm/src/provider.py +0 -116
- unrealon_llm/src/utils/__init__.py +0 -95
- unrealon_llm/src/utils/common.py +0 -64
- unrealon_llm/src/utils/data_extractor.py +0 -188
- unrealon_llm/src/utils/html_cleaner.py +0 -767
- unrealon_llm/src/utils/language_detector.py +0 -308
- unrealon_llm/src/utils/models_cache.py +0 -592
- unrealon_llm/src/utils/smart_counter.py +0 -229
- unrealon_llm/src/utils/token_counter.py +0 -189
- unrealon_sdk/README.md +0 -25
- unrealon_sdk/__init__.py +0 -30
- unrealon_sdk/pyproject.toml +0 -231
- unrealon_sdk/src/__init__.py +0 -150
- unrealon_sdk/src/cli/__init__.py +0 -12
- unrealon_sdk/src/cli/commands/__init__.py +0 -22
- unrealon_sdk/src/cli/commands/benchmark.py +0 -42
- unrealon_sdk/src/cli/commands/diagnostics.py +0 -573
- unrealon_sdk/src/cli/commands/health.py +0 -46
- unrealon_sdk/src/cli/commands/integration.py +0 -498
- unrealon_sdk/src/cli/commands/reports.py +0 -43
- unrealon_sdk/src/cli/commands/security.py +0 -36
- unrealon_sdk/src/cli/commands/server.py +0 -483
- unrealon_sdk/src/cli/commands/servers.py +0 -56
- unrealon_sdk/src/cli/commands/tests.py +0 -55
- unrealon_sdk/src/cli/main.py +0 -126
- unrealon_sdk/src/cli/utils/reporter.py +0 -519
- unrealon_sdk/src/clients/openapi.yaml +0 -3347
- unrealon_sdk/src/clients/python_http/__init__.py +0 -3
- unrealon_sdk/src/clients/python_http/api_config.py +0 -228
- unrealon_sdk/src/clients/python_http/models/BaseModel.py +0 -12
- unrealon_sdk/src/clients/python_http/models/BroadcastDeliveryStats.py +0 -33
- unrealon_sdk/src/clients/python_http/models/BroadcastMessage.py +0 -17
- unrealon_sdk/src/clients/python_http/models/BroadcastMessageRequest.py +0 -35
- unrealon_sdk/src/clients/python_http/models/BroadcastPriority.py +0 -10
- unrealon_sdk/src/clients/python_http/models/BroadcastResponse.py +0 -21
- unrealon_sdk/src/clients/python_http/models/BroadcastResultResponse.py +0 -33
- unrealon_sdk/src/clients/python_http/models/BroadcastTarget.py +0 -11
- unrealon_sdk/src/clients/python_http/models/ConnectionStats.py +0 -27
- unrealon_sdk/src/clients/python_http/models/ConnectionsResponse.py +0 -21
- unrealon_sdk/src/clients/python_http/models/DeveloperMessageResponse.py +0 -23
- unrealon_sdk/src/clients/python_http/models/ErrorResponse.py +0 -25
- unrealon_sdk/src/clients/python_http/models/HTTPValidationError.py +0 -16
- unrealon_sdk/src/clients/python_http/models/HealthResponse.py +0 -23
- unrealon_sdk/src/clients/python_http/models/HealthStatus.py +0 -33
- unrealon_sdk/src/clients/python_http/models/LogLevel.py +0 -10
- unrealon_sdk/src/clients/python_http/models/LoggingRequest.py +0 -27
- unrealon_sdk/src/clients/python_http/models/LoggingResponse.py +0 -23
- unrealon_sdk/src/clients/python_http/models/MaintenanceMode.py +0 -9
- unrealon_sdk/src/clients/python_http/models/MaintenanceModeRequest.py +0 -33
- unrealon_sdk/src/clients/python_http/models/MaintenanceStatusResponse.py +0 -39
- unrealon_sdk/src/clients/python_http/models/ParserCommandRequest.py +0 -25
- unrealon_sdk/src/clients/python_http/models/ParserMessageResponse.py +0 -21
- unrealon_sdk/src/clients/python_http/models/ParserRegistrationRequest.py +0 -28
- unrealon_sdk/src/clients/python_http/models/ParserRegistrationResponse.py +0 -25
- unrealon_sdk/src/clients/python_http/models/ParserType.py +0 -10
- unrealon_sdk/src/clients/python_http/models/ProxyBlockRequest.py +0 -19
- unrealon_sdk/src/clients/python_http/models/ProxyEndpointResponse.py +0 -20
- unrealon_sdk/src/clients/python_http/models/ProxyListResponse.py +0 -19
- unrealon_sdk/src/clients/python_http/models/ProxyProvider.py +0 -10
- unrealon_sdk/src/clients/python_http/models/ProxyPurchaseRequest.py +0 -25
- unrealon_sdk/src/clients/python_http/models/ProxyResponse.py +0 -47
- unrealon_sdk/src/clients/python_http/models/ProxyRotationRequest.py +0 -23
- unrealon_sdk/src/clients/python_http/models/ProxyStatus.py +0 -10
- unrealon_sdk/src/clients/python_http/models/ProxyUsageRequest.py +0 -19
- unrealon_sdk/src/clients/python_http/models/ProxyUsageStatsResponse.py +0 -26
- unrealon_sdk/src/clients/python_http/models/ServiceRegistrationDto.py +0 -23
- unrealon_sdk/src/clients/python_http/models/ServiceStatsResponse.py +0 -31
- unrealon_sdk/src/clients/python_http/models/SessionStartRequest.py +0 -23
- unrealon_sdk/src/clients/python_http/models/SuccessResponse.py +0 -25
- unrealon_sdk/src/clients/python_http/models/SystemNotificationResponse.py +0 -23
- unrealon_sdk/src/clients/python_http/models/ValidationError.py +0 -18
- unrealon_sdk/src/clients/python_http/models/ValidationErrorResponse.py +0 -21
- unrealon_sdk/src/clients/python_http/models/WebSocketMetrics.py +0 -21
- unrealon_sdk/src/clients/python_http/models/__init__.py +0 -44
- unrealon_sdk/src/clients/python_http/services/None_service.py +0 -35
- unrealon_sdk/src/clients/python_http/services/ParserManagement_service.py +0 -190
- unrealon_sdk/src/clients/python_http/services/ProxyManagement_service.py +0 -289
- unrealon_sdk/src/clients/python_http/services/SocketLogging_service.py +0 -187
- unrealon_sdk/src/clients/python_http/services/SystemHealth_service.py +0 -119
- unrealon_sdk/src/clients/python_http/services/WebSocketAPI_service.py +0 -198
- unrealon_sdk/src/clients/python_http/services/__init__.py +0 -0
- unrealon_sdk/src/clients/python_http/services/admin_service.py +0 -125
- unrealon_sdk/src/clients/python_http/services/async_None_service.py +0 -35
- unrealon_sdk/src/clients/python_http/services/async_ParserManagement_service.py +0 -190
- unrealon_sdk/src/clients/python_http/services/async_ProxyManagement_service.py +0 -289
- unrealon_sdk/src/clients/python_http/services/async_SocketLogging_service.py +0 -189
- unrealon_sdk/src/clients/python_http/services/async_SystemHealth_service.py +0 -123
- unrealon_sdk/src/clients/python_http/services/async_WebSocketAPI_service.py +0 -200
- unrealon_sdk/src/clients/python_http/services/async_admin_service.py +0 -125
- unrealon_sdk/src/clients/python_websocket/__init__.py +0 -28
- unrealon_sdk/src/clients/python_websocket/client.py +0 -490
- unrealon_sdk/src/clients/python_websocket/events.py +0 -732
- unrealon_sdk/src/clients/python_websocket/example.py +0 -136
- unrealon_sdk/src/clients/python_websocket/types.py +0 -871
- unrealon_sdk/src/core/__init__.py +0 -64
- unrealon_sdk/src/core/client.py +0 -556
- unrealon_sdk/src/core/config.py +0 -465
- unrealon_sdk/src/core/exceptions.py +0 -239
- unrealon_sdk/src/core/metadata.py +0 -191
- unrealon_sdk/src/core/models.py +0 -142
- unrealon_sdk/src/core/types.py +0 -68
- unrealon_sdk/src/dto/__init__.py +0 -268
- unrealon_sdk/src/dto/authentication.py +0 -108
- unrealon_sdk/src/dto/cache.py +0 -208
- unrealon_sdk/src/dto/common.py +0 -19
- unrealon_sdk/src/dto/concurrency.py +0 -393
- unrealon_sdk/src/dto/events.py +0 -108
- unrealon_sdk/src/dto/health.py +0 -339
- unrealon_sdk/src/dto/load_balancing.py +0 -336
- unrealon_sdk/src/dto/logging.py +0 -230
- unrealon_sdk/src/dto/performance.py +0 -165
- unrealon_sdk/src/dto/rate_limiting.py +0 -295
- unrealon_sdk/src/dto/resource_pooling.py +0 -128
- unrealon_sdk/src/dto/structured_logging.py +0 -112
- unrealon_sdk/src/dto/task_scheduling.py +0 -121
- unrealon_sdk/src/dto/websocket.py +0 -55
- unrealon_sdk/src/enterprise/__init__.py +0 -59
- unrealon_sdk/src/enterprise/authentication.py +0 -401
- unrealon_sdk/src/enterprise/cache_manager.py +0 -578
- unrealon_sdk/src/enterprise/error_recovery.py +0 -494
- unrealon_sdk/src/enterprise/event_system.py +0 -549
- unrealon_sdk/src/enterprise/health_monitor.py +0 -747
- unrealon_sdk/src/enterprise/load_balancer.py +0 -964
- unrealon_sdk/src/enterprise/logging/__init__.py +0 -68
- unrealon_sdk/src/enterprise/logging/cleanup.py +0 -156
- unrealon_sdk/src/enterprise/logging/development.py +0 -744
- unrealon_sdk/src/enterprise/logging/service.py +0 -410
- unrealon_sdk/src/enterprise/multithreading_manager.py +0 -853
- unrealon_sdk/src/enterprise/performance_monitor.py +0 -539
- unrealon_sdk/src/enterprise/proxy_manager.py +0 -696
- unrealon_sdk/src/enterprise/rate_limiter.py +0 -652
- unrealon_sdk/src/enterprise/resource_pool.py +0 -763
- unrealon_sdk/src/enterprise/task_scheduler.py +0 -709
- unrealon_sdk/src/internal/__init__.py +0 -10
- unrealon_sdk/src/internal/command_router.py +0 -497
- unrealon_sdk/src/internal/connection_manager.py +0 -397
- unrealon_sdk/src/internal/http_client.py +0 -446
- unrealon_sdk/src/internal/websocket_client.py +0 -420
- unrealon_sdk/src/provider.py +0 -471
- unrealon_sdk/src/utils.py +0 -234
- /unrealon_browser/{src/cli → cli}/__init__.py +0 -0
- /unrealon_browser/{src/cli → cli}/interactive_mode.py +0 -0
- /unrealon_browser/{src/cli → cli}/main.py +0 -0
- /unrealon_browser/{src/core → core}/__init__.py +0 -0
- /unrealon_browser/{src/dto → dto}/__init__.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/config.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/core.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/dataclasses.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/detection.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/enums.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/statistics.py +0 -0
- /unrealon_browser/{src/managers → managers}/__init__.py +0 -0
- /unrealon_browser/{src/managers → managers}/stealth.py +0 -0
|
@@ -1,578 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Cache Manager - Layer 3 Infrastructure Service
|
|
3
|
-
|
|
4
|
-
Intelligent caching system with TTL (Time To Live), size management,
|
|
5
|
-
and automatic eviction policies. Provides high-performance caching
|
|
6
|
-
for UnrealOn SDK components with comprehensive cache analytics.
|
|
7
|
-
|
|
8
|
-
Features:
|
|
9
|
-
- TTL-based cache expiration with automatic cleanup
|
|
10
|
-
- Size-based eviction using LRU (Least Recently Used) policy
|
|
11
|
-
- Multi-level cache hierarchy (memory, persistent)
|
|
12
|
-
- Cache hit/miss statistics and performance metrics
|
|
13
|
-
- Automatic cache warming and preloading
|
|
14
|
-
- Cache invalidation patterns and dependencies
|
|
15
|
-
- Compression support for large cached objects
|
|
16
|
-
- Thread-safe operations for concurrent access
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
import asyncio
|
|
20
|
-
import logging
|
|
21
|
-
import time
|
|
22
|
-
import threading
|
|
23
|
-
import hashlib
|
|
24
|
-
import pickle
|
|
25
|
-
import gzip
|
|
26
|
-
from typing import Dict, List, Optional, Any, Union, Callable, TypeVar, Generic
|
|
27
|
-
from datetime import datetime, timezone, timedelta
|
|
28
|
-
from enum import Enum
|
|
29
|
-
from dataclasses import dataclass, field
|
|
30
|
-
from collections import OrderedDict
|
|
31
|
-
import weakref
|
|
32
|
-
|
|
33
|
-
# Pydantic v2 for all data models
|
|
34
|
-
from pydantic import BaseModel, Field, ConfigDict
|
|
35
|
-
|
|
36
|
-
# Core SDK components
|
|
37
|
-
from unrealon_sdk.src.core.config import AdapterConfig
|
|
38
|
-
from unrealon_sdk.src.utils import generate_correlation_id
|
|
39
|
-
|
|
40
|
-
# DTO models
|
|
41
|
-
from unrealon_sdk.src.dto.logging import SDKEventType, SDKSeverity
|
|
42
|
-
from unrealon_sdk.src.dto.cache import (
|
|
43
|
-
CachePolicy,
|
|
44
|
-
CacheLevel,
|
|
45
|
-
CacheEventType,
|
|
46
|
-
CacheEntry,
|
|
47
|
-
CacheStatistics,
|
|
48
|
-
CacheConfig,
|
|
49
|
-
CacheOperation,
|
|
50
|
-
CacheMetrics,
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
# Development logging
|
|
54
|
-
from typing import TYPE_CHECKING
|
|
55
|
-
|
|
56
|
-
if TYPE_CHECKING:
|
|
57
|
-
from unrealon_sdk.src.enterprise.logging import DevelopmentLogger
|
|
58
|
-
|
|
59
|
-
logger = logging.getLogger(__name__)
|
|
60
|
-
|
|
61
|
-
T = TypeVar("T")
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
# All cache models are now imported from DTO layer
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
class CacheManager:
|
|
68
|
-
"""
|
|
69
|
-
Enterprise-grade intelligent cache manager.
|
|
70
|
-
|
|
71
|
-
Provides high-performance caching with TTL, size management,
|
|
72
|
-
and comprehensive analytics for UnrealOn SDK components.
|
|
73
|
-
"""
|
|
74
|
-
|
|
75
|
-
def __init__(
|
|
76
|
-
self,
|
|
77
|
-
config: AdapterConfig,
|
|
78
|
-
cache_config: Optional[CacheConfig] = None,
|
|
79
|
-
dev_logger: Optional["DevelopmentLogger"] = None,
|
|
80
|
-
):
|
|
81
|
-
"""Initialize cache manager."""
|
|
82
|
-
self.config = config
|
|
83
|
-
self.cache_config = cache_config or CacheConfig()
|
|
84
|
-
self.dev_logger = dev_logger
|
|
85
|
-
|
|
86
|
-
# Cache storage
|
|
87
|
-
self._cache: OrderedDict[str, CacheEntry[Any]] = OrderedDict()
|
|
88
|
-
self._lock = threading.RLock()
|
|
89
|
-
|
|
90
|
-
# Statistics
|
|
91
|
-
self.statistics = CacheStatistics(max_size=self.cache_config.max_entries)
|
|
92
|
-
|
|
93
|
-
# Background tasks
|
|
94
|
-
self._cleanup_task: Optional[asyncio.Task[None]] = None
|
|
95
|
-
self._shutdown = False
|
|
96
|
-
|
|
97
|
-
# Performance tracking
|
|
98
|
-
self._access_times: List[float] = []
|
|
99
|
-
|
|
100
|
-
# Cache warming callbacks
|
|
101
|
-
self._warm_callbacks: List[Callable[[], None]] = []
|
|
102
|
-
|
|
103
|
-
self._log_info("Cache manager initialized")
|
|
104
|
-
|
|
105
|
-
async def start(self) -> None:
|
|
106
|
-
"""Start cache manager background tasks."""
|
|
107
|
-
if self._cleanup_task is None:
|
|
108
|
-
self._cleanup_task = asyncio.create_task(self._cleanup_loop())
|
|
109
|
-
|
|
110
|
-
# Warm cache if enabled
|
|
111
|
-
if self.cache_config.warm_cache_on_startup:
|
|
112
|
-
await self._warm_cache()
|
|
113
|
-
|
|
114
|
-
self._log_info("Cache manager started")
|
|
115
|
-
|
|
116
|
-
async def stop(self) -> None:
|
|
117
|
-
"""Stop cache manager and cleanup."""
|
|
118
|
-
self._shutdown = True
|
|
119
|
-
|
|
120
|
-
if self._cleanup_task:
|
|
121
|
-
self._cleanup_task.cancel()
|
|
122
|
-
try:
|
|
123
|
-
await self._cleanup_task
|
|
124
|
-
except asyncio.CancelledError:
|
|
125
|
-
pass
|
|
126
|
-
|
|
127
|
-
self._log_info("Cache manager stopped")
|
|
128
|
-
|
|
129
|
-
def get(self, key: str, default: Optional[T] = None) -> Optional[T]:
|
|
130
|
-
"""Get value from cache."""
|
|
131
|
-
start_time = time.time()
|
|
132
|
-
|
|
133
|
-
with self._lock:
|
|
134
|
-
self.statistics.total_requests += 1
|
|
135
|
-
|
|
136
|
-
if key in self._cache:
|
|
137
|
-
entry = self._cache[key]
|
|
138
|
-
|
|
139
|
-
# Check if expired
|
|
140
|
-
if entry.is_expired():
|
|
141
|
-
self._remove_entry(key, CacheEventType.CACHE_EXPIRE)
|
|
142
|
-
self.statistics.cache_misses += 1
|
|
143
|
-
self._log_cache_event(CacheEventType.CACHE_MISS, key, "expired")
|
|
144
|
-
return default
|
|
145
|
-
|
|
146
|
-
# Update access info
|
|
147
|
-
entry.touch()
|
|
148
|
-
|
|
149
|
-
# Move to end for LRU
|
|
150
|
-
if self.cache_config.eviction_policy == CachePolicy.LRU:
|
|
151
|
-
self._cache.move_to_end(key)
|
|
152
|
-
|
|
153
|
-
self.statistics.cache_hits += 1
|
|
154
|
-
self._log_cache_event(CacheEventType.CACHE_HIT, key)
|
|
155
|
-
|
|
156
|
-
# Track access time
|
|
157
|
-
access_time = (time.time() - start_time) * 1000
|
|
158
|
-
self._track_access_time(access_time)
|
|
159
|
-
|
|
160
|
-
return entry.value
|
|
161
|
-
else:
|
|
162
|
-
self.statistics.cache_misses += 1
|
|
163
|
-
self._log_cache_event(CacheEventType.CACHE_MISS, key, "not found")
|
|
164
|
-
return default
|
|
165
|
-
|
|
166
|
-
def set(
|
|
167
|
-
self,
|
|
168
|
-
key: str,
|
|
169
|
-
value: T,
|
|
170
|
-
ttl_seconds: Optional[float] = None,
|
|
171
|
-
compress: bool = False,
|
|
172
|
-
) -> bool:
|
|
173
|
-
"""Set value in cache."""
|
|
174
|
-
try:
|
|
175
|
-
# Use default TTL if not specified
|
|
176
|
-
if ttl_seconds is None:
|
|
177
|
-
ttl_seconds = self.cache_config.default_ttl_seconds
|
|
178
|
-
|
|
179
|
-
# Clamp TTL to maximum
|
|
180
|
-
if ttl_seconds > self.cache_config.max_ttl_seconds:
|
|
181
|
-
ttl_seconds = self.cache_config.max_ttl_seconds
|
|
182
|
-
|
|
183
|
-
# Calculate size
|
|
184
|
-
size_bytes = self._calculate_size(value)
|
|
185
|
-
|
|
186
|
-
# Compress if needed
|
|
187
|
-
if compress or (
|
|
188
|
-
self.cache_config.compression_enabled
|
|
189
|
-
and size_bytes > self.cache_config.compression_threshold_bytes
|
|
190
|
-
):
|
|
191
|
-
value = self._compress_value(value)
|
|
192
|
-
compress = True
|
|
193
|
-
|
|
194
|
-
with self._lock:
|
|
195
|
-
# Check if we need to evict entries (sync version for set method)
|
|
196
|
-
self._ensure_capacity_sync()
|
|
197
|
-
|
|
198
|
-
# Create cache entry
|
|
199
|
-
entry = CacheEntry(
|
|
200
|
-
key=key,
|
|
201
|
-
value=value,
|
|
202
|
-
created_at=datetime.now(timezone.utc),
|
|
203
|
-
last_accessed=datetime.now(timezone.utc),
|
|
204
|
-
ttl_seconds=ttl_seconds,
|
|
205
|
-
compressed=compress,
|
|
206
|
-
size_bytes=size_bytes,
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
# Add to cache
|
|
210
|
-
if key in self._cache:
|
|
211
|
-
# Update existing entry
|
|
212
|
-
old_entry = self._cache[key]
|
|
213
|
-
self.statistics.memory_usage_bytes -= old_entry.size_bytes
|
|
214
|
-
|
|
215
|
-
self._cache[key] = entry
|
|
216
|
-
self.statistics.current_size = len(self._cache)
|
|
217
|
-
self.statistics.memory_usage_bytes += size_bytes
|
|
218
|
-
self.statistics.total_sets += 1
|
|
219
|
-
|
|
220
|
-
self._log_cache_event(CacheEventType.CACHE_SET, key)
|
|
221
|
-
return True
|
|
222
|
-
|
|
223
|
-
except Exception as e:
|
|
224
|
-
logger.error(f"Error setting cache key {key}: {e}")
|
|
225
|
-
return False
|
|
226
|
-
|
|
227
|
-
def delete(self, key: str) -> bool:
|
|
228
|
-
"""Delete key from cache."""
|
|
229
|
-
with self._lock:
|
|
230
|
-
if key in self._cache:
|
|
231
|
-
self._remove_entry(key, CacheEventType.CACHE_DELETE)
|
|
232
|
-
return True
|
|
233
|
-
return False
|
|
234
|
-
|
|
235
|
-
def clear(self) -> None:
|
|
236
|
-
"""Clear all cache entries."""
|
|
237
|
-
with self._lock:
|
|
238
|
-
count = len(self._cache)
|
|
239
|
-
self._cache.clear()
|
|
240
|
-
self.statistics.current_size = 0
|
|
241
|
-
self.statistics.memory_usage_bytes = 0
|
|
242
|
-
|
|
243
|
-
self._log_cache_event(CacheEventType.CACHE_CLEAR, f"cleared {count} entries")
|
|
244
|
-
|
|
245
|
-
def exists(self, key: str) -> bool:
|
|
246
|
-
"""Check if key exists in cache (without accessing it)."""
|
|
247
|
-
with self._lock:
|
|
248
|
-
if key in self._cache:
|
|
249
|
-
entry = self._cache[key]
|
|
250
|
-
return not entry.is_expired()
|
|
251
|
-
return False
|
|
252
|
-
|
|
253
|
-
def get_keys(self, pattern: Optional[str] = None) -> List[str]:
|
|
254
|
-
"""Get all cache keys, optionally filtered by pattern."""
|
|
255
|
-
with self._lock:
|
|
256
|
-
keys = list(self._cache.keys())
|
|
257
|
-
|
|
258
|
-
if pattern:
|
|
259
|
-
import fnmatch
|
|
260
|
-
|
|
261
|
-
keys = [k for k in keys if fnmatch.fnmatch(k, pattern)]
|
|
262
|
-
|
|
263
|
-
return keys
|
|
264
|
-
|
|
265
|
-
def get_statistics(self) -> CacheStatistics:
|
|
266
|
-
"""Get cache statistics."""
|
|
267
|
-
with self._lock:
|
|
268
|
-
# Update calculated stats
|
|
269
|
-
total_requests = self.statistics.cache_hits + self.statistics.cache_misses
|
|
270
|
-
if total_requests > 0:
|
|
271
|
-
self.statistics.hit_rate_percent = (
|
|
272
|
-
self.statistics.cache_hits / total_requests
|
|
273
|
-
) * 100
|
|
274
|
-
|
|
275
|
-
if self._access_times:
|
|
276
|
-
self.statistics.avg_access_time_ms = sum(self._access_times) / len(
|
|
277
|
-
self._access_times
|
|
278
|
-
)
|
|
279
|
-
|
|
280
|
-
return self.statistics.model_copy()
|
|
281
|
-
|
|
282
|
-
def reset_statistics(self) -> None:
|
|
283
|
-
"""Reset cache statistics."""
|
|
284
|
-
with self._lock:
|
|
285
|
-
self.statistics = CacheStatistics(
|
|
286
|
-
max_size=self.cache_config.max_entries,
|
|
287
|
-
current_size=len(self._cache),
|
|
288
|
-
memory_usage_bytes=self.statistics.memory_usage_bytes,
|
|
289
|
-
)
|
|
290
|
-
self._access_times.clear()
|
|
291
|
-
|
|
292
|
-
self._log_info("Cache statistics reset")
|
|
293
|
-
|
|
294
|
-
def _ensure_capacity_sync(self) -> None:
|
|
295
|
-
"""Ensure cache doesn't exceed capacity limits (synchronous version)."""
|
|
296
|
-
# Check entry count
|
|
297
|
-
max_entries = self.cache_config.max_entries
|
|
298
|
-
if len(self._cache) >= max_entries:
|
|
299
|
-
self._evict_entries_sync(int(max_entries * (1 - self.cache_config.eviction_threshold)))
|
|
300
|
-
|
|
301
|
-
# Check memory usage
|
|
302
|
-
max_memory_bytes = self.cache_config.max_memory_mb * 1024 * 1024
|
|
303
|
-
if self.statistics.memory_usage_bytes >= max_memory_bytes:
|
|
304
|
-
self._evict_by_memory_sync()
|
|
305
|
-
|
|
306
|
-
async def _ensure_capacity(self) -> None:
|
|
307
|
-
"""Ensure cache doesn't exceed capacity limits (async version)."""
|
|
308
|
-
# Check entry count
|
|
309
|
-
max_entries = self.cache_config.max_entries
|
|
310
|
-
if len(self._cache) >= max_entries:
|
|
311
|
-
await self._evict_entries(int(max_entries * (1 - self.cache_config.eviction_threshold)))
|
|
312
|
-
|
|
313
|
-
# Check memory usage
|
|
314
|
-
max_memory_bytes = self.cache_config.max_memory_mb * 1024 * 1024
|
|
315
|
-
if self.statistics.memory_usage_bytes >= max_memory_bytes:
|
|
316
|
-
await self._evict_by_memory()
|
|
317
|
-
|
|
318
|
-
async def _evict_entries(self, target_count: int) -> None:
|
|
319
|
-
"""Evict entries based on eviction policy."""
|
|
320
|
-
if len(self._cache) <= target_count:
|
|
321
|
-
return
|
|
322
|
-
|
|
323
|
-
evict_count = len(self._cache) - target_count
|
|
324
|
-
policy = self.cache_config.eviction_policy
|
|
325
|
-
|
|
326
|
-
if policy == CachePolicy.LRU:
|
|
327
|
-
# Remove least recently used (from front of OrderedDict)
|
|
328
|
-
for _ in range(evict_count):
|
|
329
|
-
if self._cache:
|
|
330
|
-
key = next(iter(self._cache))
|
|
331
|
-
self._remove_entry(key, CacheEventType.CACHE_EVICT)
|
|
332
|
-
|
|
333
|
-
elif policy == CachePolicy.LFU:
|
|
334
|
-
# Remove least frequently used
|
|
335
|
-
entries = list(self._cache.items())
|
|
336
|
-
entries.sort(key=lambda x: x[1].access_count)
|
|
337
|
-
for key, _ in entries[:evict_count]:
|
|
338
|
-
self._remove_entry(key, CacheEventType.CACHE_EVICT)
|
|
339
|
-
|
|
340
|
-
elif policy == CachePolicy.FIFO:
|
|
341
|
-
# Remove oldest entries
|
|
342
|
-
entries = list(self._cache.items())
|
|
343
|
-
entries.sort(key=lambda x: x[1].created_at)
|
|
344
|
-
for key, _ in entries[:evict_count]:
|
|
345
|
-
self._remove_entry(key, CacheEventType.CACHE_EVICT)
|
|
346
|
-
|
|
347
|
-
self._log_info(f"Evicted {evict_count} entries using {policy.value} policy")
|
|
348
|
-
|
|
349
|
-
def _evict_entries_sync(self, target_count: int) -> None:
|
|
350
|
-
"""Evict entries based on eviction policy (synchronous version)."""
|
|
351
|
-
if len(self._cache) <= target_count:
|
|
352
|
-
return
|
|
353
|
-
|
|
354
|
-
evict_count = len(self._cache) - target_count
|
|
355
|
-
policy = self.cache_config.eviction_policy
|
|
356
|
-
|
|
357
|
-
if policy == CachePolicy.LRU:
|
|
358
|
-
# Remove least recently used (from front of OrderedDict)
|
|
359
|
-
for _ in range(evict_count):
|
|
360
|
-
if self._cache:
|
|
361
|
-
key = next(iter(self._cache))
|
|
362
|
-
self._remove_entry(key, CacheEventType.CACHE_EVICT)
|
|
363
|
-
|
|
364
|
-
elif policy == CachePolicy.LFU:
|
|
365
|
-
# Remove least frequently used
|
|
366
|
-
entries = list(self._cache.items())
|
|
367
|
-
entries.sort(key=lambda x: x[1].access_count)
|
|
368
|
-
for key, _ in entries[:evict_count]:
|
|
369
|
-
self._remove_entry(key, CacheEventType.CACHE_EVICT)
|
|
370
|
-
|
|
371
|
-
elif policy == CachePolicy.FIFO:
|
|
372
|
-
# Remove oldest entries
|
|
373
|
-
entries = list(self._cache.items())
|
|
374
|
-
entries.sort(key=lambda x: x[1].created_at)
|
|
375
|
-
for key, _ in entries[:evict_count]:
|
|
376
|
-
self._remove_entry(key, CacheEventType.CACHE_EVICT)
|
|
377
|
-
|
|
378
|
-
self._log_info(f"Evicted {evict_count} entries using {policy.value} policy (sync)")
|
|
379
|
-
|
|
380
|
-
async def _evict_by_memory(self) -> None:
|
|
381
|
-
"""Evict entries to reduce memory usage."""
|
|
382
|
-
target_memory = (
|
|
383
|
-
self.cache_config.max_memory_mb * 1024 * 1024 * self.cache_config.eviction_threshold
|
|
384
|
-
)
|
|
385
|
-
|
|
386
|
-
# Sort by size (largest first) and remove until under target
|
|
387
|
-
entries = list(self._cache.items())
|
|
388
|
-
entries.sort(key=lambda x: x[1].size_bytes, reverse=True)
|
|
389
|
-
|
|
390
|
-
for key, entry in entries:
|
|
391
|
-
if self.statistics.memory_usage_bytes <= target_memory:
|
|
392
|
-
break
|
|
393
|
-
self._remove_entry(key, CacheEventType.CACHE_EVICT)
|
|
394
|
-
|
|
395
|
-
self._log_info(
|
|
396
|
-
f"Memory-based eviction completed, usage: {self.statistics.memory_usage_bytes} bytes"
|
|
397
|
-
)
|
|
398
|
-
|
|
399
|
-
def _evict_by_memory_sync(self) -> None:
|
|
400
|
-
"""Evict entries to reduce memory usage (synchronous version)."""
|
|
401
|
-
target_memory = (
|
|
402
|
-
self.cache_config.max_memory_mb * 1024 * 1024 * self.cache_config.eviction_threshold
|
|
403
|
-
)
|
|
404
|
-
|
|
405
|
-
# Sort by size (largest first) and remove until under target
|
|
406
|
-
entries = list(self._cache.items())
|
|
407
|
-
entries.sort(key=lambda x: x[1].size_bytes, reverse=True)
|
|
408
|
-
|
|
409
|
-
for key, entry in entries:
|
|
410
|
-
if self.statistics.memory_usage_bytes <= target_memory:
|
|
411
|
-
break
|
|
412
|
-
self._remove_entry(key, CacheEventType.CACHE_EVICT)
|
|
413
|
-
|
|
414
|
-
self._log_info(
|
|
415
|
-
f"Memory-based eviction completed (sync), usage: {self.statistics.memory_usage_bytes} bytes"
|
|
416
|
-
)
|
|
417
|
-
|
|
418
|
-
def _remove_entry(self, key: str, event_type: CacheEventType) -> None:
|
|
419
|
-
"""Remove entry from cache and update statistics."""
|
|
420
|
-
if key in self._cache:
|
|
421
|
-
entry = self._cache[key]
|
|
422
|
-
del self._cache[key]
|
|
423
|
-
|
|
424
|
-
self.statistics.current_size = len(self._cache)
|
|
425
|
-
self.statistics.memory_usage_bytes -= entry.size_bytes
|
|
426
|
-
|
|
427
|
-
if event_type == CacheEventType.CACHE_EVICT:
|
|
428
|
-
self.statistics.total_evictions += 1
|
|
429
|
-
elif event_type == CacheEventType.CACHE_EXPIRE:
|
|
430
|
-
self.statistics.total_expirations += 1
|
|
431
|
-
elif event_type == CacheEventType.CACHE_DELETE:
|
|
432
|
-
self.statistics.total_deletes += 1
|
|
433
|
-
|
|
434
|
-
async def _cleanup_loop(self) -> None:
|
|
435
|
-
"""Background task for cleaning up expired entries."""
|
|
436
|
-
while not self._shutdown:
|
|
437
|
-
try:
|
|
438
|
-
await asyncio.sleep(self.cache_config.cleanup_interval_seconds)
|
|
439
|
-
await self._cleanup_expired()
|
|
440
|
-
except asyncio.CancelledError:
|
|
441
|
-
break
|
|
442
|
-
except Exception as e:
|
|
443
|
-
logger.error(f"Error in cache cleanup: {e}")
|
|
444
|
-
|
|
445
|
-
async def _cleanup_expired(self) -> None:
|
|
446
|
-
"""Remove expired cache entries."""
|
|
447
|
-
expired_keys = []
|
|
448
|
-
|
|
449
|
-
with self._lock:
|
|
450
|
-
for key, entry in self._cache.items():
|
|
451
|
-
if entry.is_expired():
|
|
452
|
-
expired_keys.append(key)
|
|
453
|
-
|
|
454
|
-
for key in expired_keys:
|
|
455
|
-
with self._lock:
|
|
456
|
-
self._remove_entry(key, CacheEventType.CACHE_EXPIRE)
|
|
457
|
-
|
|
458
|
-
if expired_keys:
|
|
459
|
-
self._log_info(f"Cleaned up {len(expired_keys)} expired cache entries")
|
|
460
|
-
|
|
461
|
-
async def _warm_cache(self) -> None:
|
|
462
|
-
"""Warm cache using registered callbacks."""
|
|
463
|
-
for callback in self._warm_callbacks:
|
|
464
|
-
try:
|
|
465
|
-
callback()
|
|
466
|
-
except Exception as e:
|
|
467
|
-
logger.error(f"Error in cache warming callback: {e}")
|
|
468
|
-
|
|
469
|
-
self._log_cache_event(
|
|
470
|
-
CacheEventType.CACHE_WARM, f"warmed with {len(self._warm_callbacks)} callbacks"
|
|
471
|
-
)
|
|
472
|
-
|
|
473
|
-
def add_warm_callback(self, callback: Callable[[], None]) -> None:
|
|
474
|
-
"""Add callback for cache warming."""
|
|
475
|
-
self._warm_callbacks.append(callback)
|
|
476
|
-
|
|
477
|
-
def _calculate_size(self, value: Any) -> int:
|
|
478
|
-
"""Calculate approximate size of value in bytes."""
|
|
479
|
-
try:
|
|
480
|
-
return len(pickle.dumps(value))
|
|
481
|
-
except Exception:
|
|
482
|
-
# Fallback estimation
|
|
483
|
-
return len(str(value).encode("utf-8"))
|
|
484
|
-
|
|
485
|
-
def _compress_value(self, value: Any) -> bytes:
|
|
486
|
-
"""Compress value using gzip."""
|
|
487
|
-
try:
|
|
488
|
-
pickled = pickle.dumps(value)
|
|
489
|
-
return gzip.compress(pickled)
|
|
490
|
-
except Exception as e:
|
|
491
|
-
logger.error(f"Error compressing value: {e}")
|
|
492
|
-
return value
|
|
493
|
-
|
|
494
|
-
def _decompress_value(self, compressed_data: bytes) -> Any:
|
|
495
|
-
"""Decompress value from gzip."""
|
|
496
|
-
try:
|
|
497
|
-
decompressed = gzip.decompress(compressed_data)
|
|
498
|
-
return pickle.loads(decompressed)
|
|
499
|
-
except Exception as e:
|
|
500
|
-
logger.error(f"Error decompressing value: {e}")
|
|
501
|
-
return compressed_data
|
|
502
|
-
|
|
503
|
-
def _track_access_time(self, access_time_ms: float) -> None:
|
|
504
|
-
"""Track cache access time for statistics."""
|
|
505
|
-
self._access_times.append(access_time_ms)
|
|
506
|
-
|
|
507
|
-
# Keep only recent access times
|
|
508
|
-
if len(self._access_times) > 1000:
|
|
509
|
-
self._access_times = self._access_times[-500:]
|
|
510
|
-
|
|
511
|
-
def _log_cache_event(self, event_type: CacheEventType, key: str, details: str = "") -> None:
|
|
512
|
-
"""Log cache event."""
|
|
513
|
-
message = f"Cache {event_type.value}: {key}"
|
|
514
|
-
if details:
|
|
515
|
-
message += f" ({details})"
|
|
516
|
-
|
|
517
|
-
if self.dev_logger:
|
|
518
|
-
self.dev_logger.log_debug(
|
|
519
|
-
SDKEventType.DEBUG_CHECKPOINT,
|
|
520
|
-
message,
|
|
521
|
-
details={"cache_event": event_type.value, "key": key, "details": details},
|
|
522
|
-
)
|
|
523
|
-
else:
|
|
524
|
-
logger.debug(message)
|
|
525
|
-
|
|
526
|
-
def _log_info(self, message: str, **kwargs: Any) -> None:
|
|
527
|
-
"""Log info message."""
|
|
528
|
-
if self.dev_logger:
|
|
529
|
-
self.dev_logger.log_info(
|
|
530
|
-
SDKEventType.PERFORMANCE_OPTIMIZATION_APPLIED, message, **kwargs
|
|
531
|
-
)
|
|
532
|
-
else:
|
|
533
|
-
logger.info(message)
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
# Context manager for cache operations
|
|
537
|
-
class CacheContext:
|
|
538
|
-
"""Context manager for automatic cache operations."""
|
|
539
|
-
|
|
540
|
-
def __init__(
|
|
541
|
-
self,
|
|
542
|
-
cache_manager: CacheManager,
|
|
543
|
-
key_generator: Callable[..., str],
|
|
544
|
-
ttl_seconds: Optional[float] = None,
|
|
545
|
-
):
|
|
546
|
-
self.cache_manager = cache_manager
|
|
547
|
-
self.key_generator = key_generator
|
|
548
|
-
self.ttl_seconds = ttl_seconds
|
|
549
|
-
|
|
550
|
-
def __call__(self, func: Callable[..., T]) -> Callable[..., T]:
|
|
551
|
-
"""Decorator for automatic caching."""
|
|
552
|
-
|
|
553
|
-
def wrapper(*args, **kwargs) -> T:
|
|
554
|
-
# Generate cache key
|
|
555
|
-
cache_key = self.key_generator(*args, **kwargs)
|
|
556
|
-
|
|
557
|
-
# Try to get from cache
|
|
558
|
-
cached_result = self.cache_manager.get(cache_key)
|
|
559
|
-
if cached_result is not None:
|
|
560
|
-
return cached_result
|
|
561
|
-
|
|
562
|
-
# Execute function and cache result
|
|
563
|
-
result = func(*args, **kwargs)
|
|
564
|
-
self.cache_manager.set(cache_key, result, self.ttl_seconds)
|
|
565
|
-
|
|
566
|
-
return result
|
|
567
|
-
|
|
568
|
-
return wrapper
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
__all__ = [
|
|
572
|
-
# Main business logic class
|
|
573
|
-
"CacheManager",
|
|
574
|
-
# Utilities
|
|
575
|
-
"CacheContext",
|
|
576
|
-
# Note: Cache models (CacheConfig, CacheEntry, CacheStatistics, etc.)
|
|
577
|
-
# are available via DTO imports: from unrealon_sdk.src.dto.cache import ...
|
|
578
|
-
]
|