lumen-app 0.4.2__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.
- lumen_app/__init__.py +7 -0
- lumen_app/core/__init__.py +0 -0
- lumen_app/core/config.py +661 -0
- lumen_app/core/installer.py +274 -0
- lumen_app/core/loader.py +45 -0
- lumen_app/core/router.py +87 -0
- lumen_app/core/server.py +389 -0
- lumen_app/core/service.py +49 -0
- lumen_app/core/tests/__init__.py +1 -0
- lumen_app/core/tests/test_core_integration.py +561 -0
- lumen_app/core/tests/test_env_checker.py +487 -0
- lumen_app/proto/README.md +12 -0
- lumen_app/proto/ml_service.proto +88 -0
- lumen_app/proto/ml_service_pb2.py +66 -0
- lumen_app/proto/ml_service_pb2.pyi +136 -0
- lumen_app/proto/ml_service_pb2_grpc.py +251 -0
- lumen_app/server.py +362 -0
- lumen_app/utils/env_checker.py +752 -0
- lumen_app/utils/installation/__init__.py +25 -0
- lumen_app/utils/installation/env_manager.py +152 -0
- lumen_app/utils/installation/micromamba_installer.py +459 -0
- lumen_app/utils/installation/package_installer.py +149 -0
- lumen_app/utils/installation/verifier.py +95 -0
- lumen_app/utils/logger.py +181 -0
- lumen_app/utils/mamba/cuda.yaml +12 -0
- lumen_app/utils/mamba/default.yaml +6 -0
- lumen_app/utils/mamba/openvino.yaml +7 -0
- lumen_app/utils/mamba/tensorrt.yaml +13 -0
- lumen_app/utils/package_resolver.py +309 -0
- lumen_app/utils/preset_registry.py +219 -0
- lumen_app/web/__init__.py +3 -0
- lumen_app/web/api/__init__.py +1 -0
- lumen_app/web/api/config.py +229 -0
- lumen_app/web/api/hardware.py +201 -0
- lumen_app/web/api/install.py +608 -0
- lumen_app/web/api/server.py +253 -0
- lumen_app/web/core/__init__.py +1 -0
- lumen_app/web/core/server_manager.py +348 -0
- lumen_app/web/core/state.py +264 -0
- lumen_app/web/main.py +145 -0
- lumen_app/web/models/__init__.py +28 -0
- lumen_app/web/models/config.py +63 -0
- lumen_app/web/models/hardware.py +64 -0
- lumen_app/web/models/install.py +134 -0
- lumen_app/web/models/server.py +95 -0
- lumen_app/web/static/assets/index-CGuhGHC9.css +1 -0
- lumen_app/web/static/assets/index-DN6HmxWS.js +56 -0
- lumen_app/web/static/index.html +14 -0
- lumen_app/web/static/vite.svg +1 -0
- lumen_app/web/websockets/__init__.py +1 -0
- lumen_app/web/websockets/logs.py +159 -0
- lumen_app-0.4.2.dist-info/METADATA +23 -0
- lumen_app-0.4.2.dist-info/RECORD +56 -0
- lumen_app-0.4.2.dist-info/WHEEL +5 -0
- lumen_app-0.4.2.dist-info/entry_points.txt +3 -0
- lumen_app-0.4.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""Lumen package installer for installing Lumen packages from GitHub Releases."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import subprocess
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Callable
|
|
7
|
+
|
|
8
|
+
from lumen_resources.lumen_config import Region
|
|
9
|
+
|
|
10
|
+
from ...core.config import DeviceConfig
|
|
11
|
+
from ..package_resolver import GitHubPackageResolver, LumenPackageResolver
|
|
12
|
+
from .env_manager import PythonEnvManager
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class LumenPackageInstaller:
|
|
18
|
+
"""Installs Lumen packages from GitHub Releases."""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
env_manager: PythonEnvManager,
|
|
23
|
+
github_resolver: GitHubPackageResolver,
|
|
24
|
+
log_callback: Callable[[str], None] | None = None,
|
|
25
|
+
):
|
|
26
|
+
"""Initialize package installer.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
env_manager: Python environment manager
|
|
30
|
+
github_resolver: GitHub package resolver
|
|
31
|
+
log_callback: Optional callback for log messages
|
|
32
|
+
"""
|
|
33
|
+
self.env_manager = env_manager
|
|
34
|
+
self.github_resolver = github_resolver
|
|
35
|
+
self.log_callback = log_callback or (lambda msg: None)
|
|
36
|
+
|
|
37
|
+
logger.debug("[LumenPackageInstaller] Initialized")
|
|
38
|
+
|
|
39
|
+
def install_packages(
|
|
40
|
+
self,
|
|
41
|
+
packages: list[str],
|
|
42
|
+
device_config: DeviceConfig,
|
|
43
|
+
region: Region,
|
|
44
|
+
) -> None:
|
|
45
|
+
"""Install Lumen packages from GitHub Releases.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
packages: List of package names (e.g., ["lumen_ocr", "lumen_clip"])
|
|
49
|
+
device_config: Device configuration with dependency metadata
|
|
50
|
+
region: Region for mirror selection
|
|
51
|
+
|
|
52
|
+
Raises:
|
|
53
|
+
Exception: If installation fails
|
|
54
|
+
"""
|
|
55
|
+
logger.info(f"[LumenPackageInstaller] Installing packages: {packages}")
|
|
56
|
+
self.log_callback(f"Installing {len(packages)} Lumen package(s)...")
|
|
57
|
+
|
|
58
|
+
# Create wheel download directory
|
|
59
|
+
cache_dir = self.env_manager.cache_dir
|
|
60
|
+
wheel_dir = cache_dir / "wheels"
|
|
61
|
+
wheel_dir.mkdir(parents=True, exist_ok=True)
|
|
62
|
+
|
|
63
|
+
# Download all wheels first and collect paths
|
|
64
|
+
self.log_callback("Downloading package wheels...")
|
|
65
|
+
wheel_paths = {} # {package_name: wheel_path}
|
|
66
|
+
for package in packages:
|
|
67
|
+
wheel_path = self._download_package_wheel(package, wheel_dir)
|
|
68
|
+
wheel_paths[package] = wheel_path
|
|
69
|
+
|
|
70
|
+
# Build pip install command
|
|
71
|
+
self.log_callback("Building installation command...")
|
|
72
|
+
pip_args = LumenPackageResolver.build_pip_install_args(
|
|
73
|
+
packages=packages,
|
|
74
|
+
device_config=device_config,
|
|
75
|
+
region=region,
|
|
76
|
+
wheel_paths=wheel_paths,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Run pip install
|
|
80
|
+
self.log_callback("Running pip install...")
|
|
81
|
+
self._run_pip_install(pip_args)
|
|
82
|
+
|
|
83
|
+
self.log_callback("All packages installed successfully")
|
|
84
|
+
logger.info("[LumenPackageInstaller] All packages installed")
|
|
85
|
+
|
|
86
|
+
def _download_package_wheel(self, package: str, wheel_dir: Path) -> Path:
|
|
87
|
+
"""Download package wheel from GitHub Releases.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
package: Package name (e.g., "lumen_ocr")
|
|
91
|
+
wheel_dir: Directory to save wheel files
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Path to downloaded wheel
|
|
95
|
+
|
|
96
|
+
Raises:
|
|
97
|
+
Exception: If download fails
|
|
98
|
+
"""
|
|
99
|
+
logger.debug(f"[LumenPackageInstaller] Downloading wheel for {package}")
|
|
100
|
+
self.log_callback(f"Downloading {package}...")
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
url, version = self.github_resolver.resolve_package_url(package)
|
|
104
|
+
logger.debug(f"[LumenPackageInstaller] Resolved {package} {version}: {url}")
|
|
105
|
+
|
|
106
|
+
wheel_path = self.github_resolver.download_wheel(
|
|
107
|
+
url, wheel_dir, self.log_callback
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
logger.info(f"[LumenPackageInstaller] Downloaded {package}: {wheel_path}")
|
|
111
|
+
return wheel_path
|
|
112
|
+
|
|
113
|
+
except Exception as e:
|
|
114
|
+
logger.error(f"[LumenPackageInstaller] Failed to download {package}: {e}")
|
|
115
|
+
self.log_callback(f"Failed to download {package}: {e}")
|
|
116
|
+
raise Exception(f"Failed to download {package}: {e}")
|
|
117
|
+
|
|
118
|
+
def _run_pip_install(self, pip_args: list[str]) -> None:
|
|
119
|
+
"""Run pip install command.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
pip_args: Pip command arguments
|
|
123
|
+
|
|
124
|
+
Raises:
|
|
125
|
+
Exception: If installation fails
|
|
126
|
+
"""
|
|
127
|
+
cmd_str = "pip " + " ".join(pip_args[:3]) + " ..."
|
|
128
|
+
logger.debug(f"[LumenPackageInstaller] Running: {cmd_str}")
|
|
129
|
+
self.log_callback("Running: pip install ...")
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
result = self.env_manager.run_pip(*pip_args)
|
|
133
|
+
|
|
134
|
+
if result.returncode != 0:
|
|
135
|
+
error_msg = result.stderr or result.stdout
|
|
136
|
+
logger.error(f"[LumenPackageInstaller] Pip install failed: {error_msg}")
|
|
137
|
+
self.log_callback(f"Installation failed:\n{error_msg}")
|
|
138
|
+
raise Exception(f"Pip install failed: {error_msg}")
|
|
139
|
+
|
|
140
|
+
# Log output
|
|
141
|
+
if result.stdout:
|
|
142
|
+
logger.debug(f"[LumenPackageInstaller] Pip output:\n{result.stdout}")
|
|
143
|
+
|
|
144
|
+
logger.info("[LumenPackageInstaller] Pip install successful")
|
|
145
|
+
|
|
146
|
+
except subprocess.TimeoutExpired:
|
|
147
|
+
logger.error("[LumenPackageInstaller] Pip install timed out")
|
|
148
|
+
self.log_callback("Installation timed out")
|
|
149
|
+
raise Exception("Pip install timed out")
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Installation verifier for validating Lumen package installations."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import subprocess
|
|
5
|
+
|
|
6
|
+
from .env_manager import PythonEnvManager
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class InstallationVerifier:
|
|
12
|
+
"""Verifies Lumen package installations."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, env_manager: PythonEnvManager):
|
|
15
|
+
"""Initialize verifier.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
env_manager: Python environment manager
|
|
19
|
+
"""
|
|
20
|
+
self.env_manager = env_manager
|
|
21
|
+
logger.debug("[InstallationVerifier] Initialized")
|
|
22
|
+
|
|
23
|
+
def verify_imports(self, packages: list[str]) -> dict[str, bool]:
|
|
24
|
+
"""Verify that packages can be imported.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
packages: List of package names (e.g., ["lumen_ocr", "lumen_clip"])
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Dictionary mapping package names to import success status
|
|
31
|
+
"""
|
|
32
|
+
logger.info(f"[InstallationVerifier] Verifying imports for: {packages}")
|
|
33
|
+
results = {}
|
|
34
|
+
|
|
35
|
+
for package in packages:
|
|
36
|
+
# Convert lumen-ocr to lumen_ocr
|
|
37
|
+
module_name = package.replace("-", "_")
|
|
38
|
+
success = self._verify_single_import(module_name)
|
|
39
|
+
results[package] = success
|
|
40
|
+
|
|
41
|
+
status = "✓" if success else "✗"
|
|
42
|
+
logger.info(f"[InstallationVerifier] {status} {package}")
|
|
43
|
+
|
|
44
|
+
return results
|
|
45
|
+
|
|
46
|
+
def _verify_single_import(self, module_name: str) -> bool:
|
|
47
|
+
"""Verify that a single module can be imported.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
module_name: Module name (e.g., "lumen_ocr")
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
True if import successful
|
|
54
|
+
"""
|
|
55
|
+
cmd = [
|
|
56
|
+
str(self.env_manager.micromamba_exe),
|
|
57
|
+
"run",
|
|
58
|
+
"-n",
|
|
59
|
+
PythonEnvManager.ENV_NAME,
|
|
60
|
+
"python",
|
|
61
|
+
"-c",
|
|
62
|
+
f"from {module_name} import *",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
logger.debug(f"[InstallationVerifier] Verifying import: {module_name}")
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
|
69
|
+
|
|
70
|
+
if result.returncode == 0:
|
|
71
|
+
logger.debug(f"[InstallationVerifier] Import successful: {module_name}")
|
|
72
|
+
return True
|
|
73
|
+
else:
|
|
74
|
+
logger.warning(
|
|
75
|
+
f"[InstallationVerifier] Import failed for {module_name}: "
|
|
76
|
+
f"{result.stderr[:200]}"
|
|
77
|
+
)
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
except subprocess.TimeoutExpired:
|
|
81
|
+
logger.error(f"[InstallationVerifier] Import timed out: {module_name}")
|
|
82
|
+
return False
|
|
83
|
+
except Exception as e:
|
|
84
|
+
logger.error(f"[InstallationVerifier] Import error for {module_name}: {e}")
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
def verify_env_exists(self) -> bool:
|
|
88
|
+
"""Verify that lumen_env exists.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
True if environment exists
|
|
92
|
+
"""
|
|
93
|
+
exists = self.env_manager.env_exists()
|
|
94
|
+
logger.info(f"[InstallationVerifier] Environment exists: {exists}")
|
|
95
|
+
return exists
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""Centralized logging configuration for Lumen applications.
|
|
2
|
+
|
|
3
|
+
This module provides a unified logging setup with colorized console output
|
|
4
|
+
and optional file logging. Usage:
|
|
5
|
+
|
|
6
|
+
from lumen_app.utils.logger import get_logger
|
|
7
|
+
|
|
8
|
+
logger = get_logger(__name__)
|
|
9
|
+
logger.info("Application started")
|
|
10
|
+
|
|
11
|
+
Or use the predefined loggers:
|
|
12
|
+
|
|
13
|
+
from lumen_app.utils.logger import lumen_logger
|
|
14
|
+
|
|
15
|
+
lumen_logger.info("Using predefined logger")
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import logging
|
|
19
|
+
import sys
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Optional
|
|
22
|
+
|
|
23
|
+
# Try to import colorlog, fallback to standard logging
|
|
24
|
+
try:
|
|
25
|
+
import colorlog
|
|
26
|
+
|
|
27
|
+
COLORLOG_AVAILABLE = True
|
|
28
|
+
except ImportError:
|
|
29
|
+
COLORLOG_AVAILABLE = False
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Color scheme for different log levels
|
|
33
|
+
LOG_COLORS = {
|
|
34
|
+
"DEBUG": "cyan",
|
|
35
|
+
"INFO": "green",
|
|
36
|
+
"WARNING": "yellow",
|
|
37
|
+
"ERROR": "red",
|
|
38
|
+
"CRITICAL": "red,bg_white",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
LOG_FORMAT = "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
|
|
42
|
+
DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def setup_logging(
|
|
46
|
+
level: int = logging.INFO,
|
|
47
|
+
log_file: Optional[Path] = None,
|
|
48
|
+
enable_colors: bool = True,
|
|
49
|
+
) -> None:
|
|
50
|
+
"""Setup root logging configuration.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
level: Logging level (default: INFO)
|
|
54
|
+
log_file: Optional path to log file
|
|
55
|
+
enable_colors: Whether to enable colored output (requires colorlog)
|
|
56
|
+
"""
|
|
57
|
+
# Get root logger
|
|
58
|
+
root_logger = logging.getLogger()
|
|
59
|
+
root_logger.setLevel(level)
|
|
60
|
+
|
|
61
|
+
# Remove existing handlers
|
|
62
|
+
root_logger.handlers.clear()
|
|
63
|
+
|
|
64
|
+
# Console handler
|
|
65
|
+
if COLORLOG_AVAILABLE and enable_colors:
|
|
66
|
+
console_handler = colorlog.StreamHandler(sys.stdout)
|
|
67
|
+
console_formatter = colorlog.ColoredFormatter(
|
|
68
|
+
"%(log_color)s%(asctime)s [%(levelname)8s] %(name)s:%(reset)s %(message)s",
|
|
69
|
+
datefmt=DATE_FORMAT,
|
|
70
|
+
log_colors=LOG_COLORS,
|
|
71
|
+
)
|
|
72
|
+
else:
|
|
73
|
+
console_handler = logging.StreamHandler(sys.stdout)
|
|
74
|
+
console_formatter = logging.Formatter(LOG_FORMAT, datefmt=DATE_FORMAT)
|
|
75
|
+
|
|
76
|
+
console_handler.setFormatter(console_formatter)
|
|
77
|
+
console_handler.setLevel(level)
|
|
78
|
+
root_logger.addHandler(console_handler)
|
|
79
|
+
|
|
80
|
+
# File handler (optional)
|
|
81
|
+
if log_file:
|
|
82
|
+
log_file.parent.mkdir(parents=True, exist_ok=True)
|
|
83
|
+
file_handler = logging.FileHandler(log_file, encoding="utf-8")
|
|
84
|
+
file_formatter = logging.Formatter(LOG_FORMAT, datefmt=DATE_FORMAT)
|
|
85
|
+
file_handler.setFormatter(file_formatter)
|
|
86
|
+
file_handler.setLevel(level)
|
|
87
|
+
root_logger.addHandler(file_handler)
|
|
88
|
+
|
|
89
|
+
# Suppress verbose third-party loggers
|
|
90
|
+
_suppress_third_party_loggers()
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _suppress_third_party_loggers() -> None:
|
|
94
|
+
"""Suppress verbose debug logging from third-party libraries."""
|
|
95
|
+
# List of loggers to suppress
|
|
96
|
+
suppressed_loggers = {
|
|
97
|
+
"flet": logging.WARNING,
|
|
98
|
+
"flet_core": logging.WARNING,
|
|
99
|
+
"flet.view": logging.WARNING,
|
|
100
|
+
"flet_web": logging.WARNING,
|
|
101
|
+
"flet_web.fastapi": logging.WARNING,
|
|
102
|
+
"httpx": logging.WARNING,
|
|
103
|
+
"urllib3": logging.WARNING,
|
|
104
|
+
"websockets": logging.WARNING,
|
|
105
|
+
"asyncio": logging.WARNING,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
for logger_name, logger_level in suppressed_loggers.items():
|
|
109
|
+
third_party_logger = logging.getLogger(logger_name)
|
|
110
|
+
third_party_logger.setLevel(logger_level)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def get_logger(name: str) -> logging.Logger:
|
|
114
|
+
"""Get a logger instance with the given name.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
name: Logger name (typically __name__ from the calling module)
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Logger instance
|
|
121
|
+
|
|
122
|
+
Example:
|
|
123
|
+
logger = get_logger(__name__)
|
|
124
|
+
logger.info("Message")
|
|
125
|
+
"""
|
|
126
|
+
return logging.getLogger(name)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# Predefined loggers for common modules
|
|
130
|
+
def _get_module_logger(module_name: str) -> logging.Logger:
|
|
131
|
+
"""Get or create a logger for a specific module."""
|
|
132
|
+
logger = logging.getLogger(module_name)
|
|
133
|
+
# Ensure logger has a level set (root level will be used if not set)
|
|
134
|
+
if not logger.setLevel:
|
|
135
|
+
logger.setLevel(logging.DEBUG)
|
|
136
|
+
return logger
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# Export predefined loggers
|
|
140
|
+
lumen_logger = _get_module_logger("lumen")
|
|
141
|
+
config_logger = _get_module_logger("lumen.config")
|
|
142
|
+
env_checker_logger = _get_module_logger("lumen.env_checker")
|
|
143
|
+
installer_logger = _get_module_logger("lumen.installer")
|
|
144
|
+
ui_logger = _get_module_logger("lumen.ui")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# Auto-setup on import (can be called explicitly if needed)
|
|
148
|
+
def initialize(
|
|
149
|
+
level: int = logging.INFO,
|
|
150
|
+
log_file: Optional[Path] = None,
|
|
151
|
+
log_dir: Optional[Path] = None,
|
|
152
|
+
) -> None:
|
|
153
|
+
"""Initialize logging system.
|
|
154
|
+
|
|
155
|
+
This is called automatically on import with default settings.
|
|
156
|
+
Call it explicitly to customize logging behavior.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
level: Logging level (e.g., logging.DEBUG, logging.INFO)
|
|
160
|
+
log_file: Specific log file path
|
|
161
|
+
log_dir: Directory for log files (alternative to log_file)
|
|
162
|
+
|
|
163
|
+
Example:
|
|
164
|
+
# Initialize with DEBUG level and custom log file
|
|
165
|
+
initialize(
|
|
166
|
+
level=logging.DEBUG,
|
|
167
|
+
log_file=Path("~/lumen_logs/app.log")
|
|
168
|
+
)
|
|
169
|
+
"""
|
|
170
|
+
# Determine log file path
|
|
171
|
+
if log_file is None and log_dir is not None:
|
|
172
|
+
from datetime import datetime
|
|
173
|
+
|
|
174
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
175
|
+
log_file = log_dir / f"lumen_{timestamp}.log"
|
|
176
|
+
|
|
177
|
+
setup_logging(level=level, log_file=log_file, enable_colors=COLORLOG_AVAILABLE)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
# Auto-initialize with defaults
|
|
181
|
+
initialize(level=logging.INFO)
|