diff --git a/main.py b/main.py index c478f1e..fa00987 100644 --- a/main.py +++ b/main.py @@ -12,8 +12,10 @@ from voxkit.config.app_config import AppConfig, get_app_config, get_profile_config_path from voxkit.config.logging_config import setup_logging -# Disable Qt emoji support to prevent crashes in frozen builds - +# Minimal early config so frozen-env messages below are emitted before +# setup_logging() runs in main(); setup_logging() will reconfigure handlers. +logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s") +log = logging.getLogger("voxkit.main") # CRITICAL: Must be at the top for frozen apps using multiprocessing if __name__ == "__main__": @@ -55,10 +57,10 @@ if conda_bin: existing_path = os.environ.get('PATH', '/usr/bin:/bin:/usr/sbin:/sbin') minimal_env['PATH'] = f"{conda_bin}:{existing_path}" - print(f"[FROZEN] Added conda to PATH: {conda_bin}") + log.info("[FROZEN] Added conda to PATH: %s", conda_bin) else: minimal_env['PATH'] = os.environ.get('PATH', '/usr/bin:/bin:/usr/sbin:/sbin') - print("[FROZEN] Warning: conda not found in standard locations. MFA alignment may fail.") + log.warning("[FROZEN] conda not found in standard locations. MFA alignment may fail.") # PyInstaller-specific: Add Qt plugin paths if getattr(sys, '_MEIPASS', None): @@ -67,30 +69,26 @@ if os.path.exists(qt_plugins): minimal_env['QT_PLUGIN_PATH'] = qt_plugins minimal_env['QT_QPA_PLATFORM_PLUGIN_PATH'] = os.path.join(qt_plugins, 'platforms') - + # Additional Qt environment for frozen apps minimal_env['QT_AUTO_SCREEN_SCALE_FACTOR'] = '1' minimal_env['QT_LOGGING_RULES'] = '*.debug=false;qt.qpa.*=false' - - print(f"[FROZEN] Qt plugins directory: {qt_plugins}") - print(f"[FROZEN] Bundle directory: {bundle_dir}") + + log.info("[FROZEN] Qt plugins directory: %s", qt_plugins) + log.info("[FROZEN] Bundle directory: %s", bundle_dir) # IMPORTANT: Don't clear os.environ - preserve system environment # Just add/override our minimal required variables for key, value in minimal_env.items(): if value: os.environ[key] = value - - print("[FROZEN] Environment configured for frozen app") - + log.info("[FROZEN] Environment configured for frozen app") from PyQt6.QtWidgets import QApplication from voxkit.config import STARTUP_SCRIPT -from voxkit.gui import AlignmentGUI +from voxkit.gui import VoxKitGUI from voxkit.gui.workers.startup import execute_startup_script -from pathlib import Path - def main(): # Initialize logging as early as possible so startup work is captured. @@ -108,7 +106,6 @@ def main(): from voxkit.gui.components.log_handler import get_gui_log_handler get_gui_log_handler() - log = logging.getLogger("voxkit.main") log.info("VoxKit starting (frozen=%s)", bool(getattr(sys, "frozen", False))) app = QApplication(sys.argv) @@ -128,7 +125,7 @@ def main(): app_config = AppConfig.from_yaml(profile_path / "app_info.yaml") pipeline_config = PipelineConfig.from_yaml(profile_path / "pipeline_definitions.yaml") - window = AlignmentGUI(pipeline_config=pipeline_config, app_config=app_config) + window = VoxKitGUI(pipeline_config=pipeline_config, app_config=app_config) window.show() log.info("Main window shown, entering Qt event loop") sys.exit(app.exec()) @@ -136,6 +133,5 @@ def main(): if __name__ == "__main__": # Prevent multiprocessing from spawning new app windows in frozen builds - multiprocessing.freeze_support() multiprocessing.set_start_method('spawn', force=True) main() diff --git a/src/voxkit/__init__.py b/src/voxkit/__init__.py index b3e3b54..1ae30d9 100644 --- a/src/voxkit/__init__.py +++ b/src/voxkit/__init__.py @@ -1,20 +1,17 @@ -"""VoxKit - Speech Analysis and Forced Alignment Toolkit. - -A desktop application bridging AI/ML research and clinical speech-language -pathology. Provides accessible interfaces to forced alignment engines and -flexible dataset analysis tools. +"""Modules for VoxKit; Provides accessible interfaces to forced alignment engines and +flexible dataset analysis and management tools. Subpackages ----------- -- **engines**: Speech toolkit backends (MFA, Faster-Whisper) +- **engines**: Speech toolkit backends (MFA, W2TG. FasterWhisper, etc.) - **analyzers**: Dataset metadata extraction - **storage**: Persistence for datasets, models, and alignments - **gui**: PyQt6 desktop interface - **config**: Application and pipeline configuration """ -__version__ = "0.1.0" -__author__ = "Beckett Frey @beckettfrey.com" +__version__ = "0.4.0" +__author__ = "Beckett Frey - code@beckettfrey.com" # Import subpackages for pdoc discoverability (not re-exported in __all__) from . import analyzers, config, engines, gui, storage diff --git a/src/voxkit/analyzers/__init__.py b/src/voxkit/analyzers/__init__.py index c270eb6..4f29df5 100644 --- a/src/voxkit/analyzers/__init__.py +++ b/src/voxkit/analyzers/__init__.py @@ -1,7 +1,5 @@ -"""VoxKit Analyzers Module. - -Analyzers observe datasets and link metadata in flexible, abstract ways. They -extract structured information at registration time, producing CSV summaries +"""Analyzers observe datasets and link metadata. They +extract structured information at registration time (ingestion), producing CSV summaries that can be visualized within VoxKit without rescanning the filesystem. API @@ -9,11 +7,10 @@ - **AnalyzerManager.list_analyzers**: List registered analyzer IDs - **AnalyzerManager.get_analyzer**: Retrieve analyzer instance by ID - **AnalyzerManager.get_analyzers**: Get all registered analyzers -- **DatasetAnalyzer**: Abstract base class for all analyzers Available Analyzers ------------------- -**DefaultAnalyzer** (``default_analyzer.py``) +**DefaultAnalyzer** Extracts speaker count and audio file counts per speaker directory. Includes a bar chart visualization for quick dataset overview. @@ -28,7 +25,7 @@ Notes ----- -- Analyzers run during dataset registration +- Analyzers run during dataset registration (ingestion) - Each analyzer's ``name`` property serves as its unique identifier - Output is a list of dicts where keys become CSV column headers - Custom visualizations can be provided via the ``visualize`` method @@ -78,7 +75,4 @@ def get_analyzer(self, analyzer_id: str) -> DatasetAnalyzer: __all__ = [ "ManageAnalyzers", - "AnalyzerManager", - "DatasetAnalyzer", - "DefaultAnalyzer", ] diff --git a/src/voxkit/config/__init__.py b/src/voxkit/config/__init__.py index 46cede6..0cc437e 100644 --- a/src/voxkit/config/__init__.py +++ b/src/voxkit/config/__init__.py @@ -1,6 +1,15 @@ -"""VoxKit configuration module. +"""This module provides access to configurable info about the application. -This module provides access to application and pipeline configurations. +Configurations +-------------- +- App config: application metadata and provenance (``app_config``). +- Pipeline config: pipeline steps and UI wiring (``pipeline_config``). +- Startup config: launch-time constants and defaults (``startup_config``). +- Logging config: rotating file logger setup (``logging_config``). + +Only ``app_config`` and ``pipeline_config`` are dynamic post-build; they are +loaded from YAML files under the active profile and can change without a +rebuild. ``startup_config`` and ``logging_config`` are baked in at build time. Profile System -------------- @@ -19,6 +28,11 @@ get_profile_config_path, resolve_config_file, ) +from voxkit.config.logging_config import ( + LOG_FILE, + reset_logging, + setup_logging, +) from voxkit.config.pipeline_config import ( PipelineConfig, PipelineStep, @@ -55,4 +69,8 @@ "Defaults", "Mode", "STARTUP_SCRIPT", + # Logging config + "LOG_FILE", + "setup_logging", + "reset_logging", ] diff --git a/src/voxkit/engines/__init__.py b/src/voxkit/engines/__init__.py index 16421ab..04d5aed 100644 --- a/src/voxkit/engines/__init__.py +++ b/src/voxkit/engines/__init__.py @@ -1,30 +1,28 @@ -"""VoxKit Engines Module. - -Engines are speech toolkit backends that perform alignment, training, and -transcription operations. Each engine provides one or more tools with -configurable settings. +"""Engines are speech toolkit backends. Each engine provides one or more tools +where each tool is a unit of functionality (e.g. alignment, training, transcription). API --- - **EngineManager.list_engines**: List registered engine IDs - **EngineManager.get_engine**: Retrieve engine instance by ID - **EngineManager.get_tool_providers**: Get engines providing a specific tool type -- **AlignmentEngine**: Abstract base class for all engines -- **ToolType**: Literal type for tool categories ("train", "align", "transcribe") +- **ToolType**: Literal type for compatible tool types Available Engines ----------------- -**MFAEngine** (``mfa_engine.py``) +**MFAEngine** Montreal Forced Aligner integration. Provides alignment using pretrained acoustic models and training via model adaptation. + Tools: ``alignment``, ``training`` -**FasterWhisperEngine** (``faster_whisper_engine.py``) - Faster-Whisper integration for transcription. Produces .lab label files - from audio using CTranslate2 backend. +**W2TGEngine** + Wav2TextGrid integration using Wav2Vec 2.0 models. + Tools: ``alignment``, ``training`` -**W2TGEngine** (``w2tg_engine.py``) [disabled] - Wav2TextGrid integration using Wav2Vec 2.0 models. Supports both - alignment and from-scratch training. +**FasterWhisperEngine** + Faster-Whisper integration for transcription. Produces .lab transcript files + from audio using CTranslate2 backend. + Tools: ``transcription`` Storage Structure ----------------- @@ -60,7 +58,7 @@ class EngineManager: """ Manager class for registered engines. - Provides a unified interface to list and retrieve registered alignment engines. + Provides a unified interface to list and retrieve engines. Methods: list_engines(): Return a list of registered engine IDs. @@ -99,11 +97,4 @@ def get_tool_providers(self, tool: ToolType) -> dict[str, AlignmentEngine]: faster_whisper = FasterWhisperEngine(id="FASTERWHISPERENGINE") engines = EngineManager({mfa.id: mfa, faster_whisper.id: faster_whisper, w2tg.id: w2tg}) -__all__ = [ - "engines", - "EngineManager", - "AlignmentEngine", - "ToolType", - "MFAEngine", - "FasterWhisperEngine", -] +__all__ = ["engines", "ToolType"] diff --git a/src/voxkit/gui/__init__.py b/src/voxkit/gui/__init__.py index 3f8e3d8..54a6345 100644 --- a/src/voxkit/gui/__init__.py +++ b/src/voxkit/gui/__init__.py @@ -1,10 +1,9 @@ -"""VoxKit GUI Module. - -PyQt6-based graphical user interface for the VoxKit desktop application. +"""PyQt6-based graphical user interface for interacting with datasets using tools. +Designed for ease of use and extensibility. API --- -- **AlignmentGUI**: Main application window with toolbar navigation +- **VoxKitGUI**: Main application window with toolbar navigation Submodules ---------- @@ -167,13 +166,13 @@ """ -class AlignmentGUI(QMainWindow): +class VoxKitGUI(QMainWindow): def __init__( self, app_config: Optional[AppConfig] = None, pipeline_config: Optional[PipelineConfig] = None, ): - """Initialize the AlignmentGUI. + """Initialize the VoxKitGUI. Args: app_config: Application configuration. If None, loads default from config files. @@ -186,7 +185,7 @@ def __init__( self.pipeline_config = pipeline_config or get_pipeline_config() logger.info( - "AlignmentGUI initialized: app=%s version=%s", + "VoxKitGUI initialized: app=%s version=%s", self.app_config.app_name, self.app_config.version, ) @@ -450,4 +449,4 @@ def _open_log_viewer(self) -> None: self._log_viewer.activateWindow() -__all__ = ["AlignmentGUI"] +__all__ = ["VoxKitGUI"] diff --git a/src/voxkit/gui/components/__init__.py b/src/voxkit/gui/components/__init__.py index 40dcbb2..308c436 100644 --- a/src/voxkit/gui/components/__init__.py +++ b/src/voxkit/gui/components/__init__.py @@ -1,6 +1,4 @@ -"""Components Module. - -Reusable PyQt6 widgets used throughout the VoxKit application. +"""Reusable PyQt6 widgets taiored functionally and stylistically for the VoxKit application. API --- @@ -10,6 +8,9 @@ - **GripSplitter**: QSplitter with visible grip handle for intuitive resizing - **HuggingFaceButton**: Branded button with HuggingFace logo - **LoadingDialog**: Splash screen / loading dialog with animated spinner +- **LogViewerDialog**: Dialog for viewing live application logs +- **QObjectLogHandler**: Qt-aware logging handler that emits records as Qt signals +- **get_gui_log_handler**: Accessor for the singleton GUI log handler - **ModelSelectionPanel**: Combined engine and model selection panel - **MultiColumnComboBox**: QComboBox with multi-column dropdown table display - **OverlayWidget**: Semi-transparent overlay for modal blur effects @@ -29,6 +30,7 @@ from .grip_splitter import GripSplitter from .huggingface_button import HuggingFaceButton from .loading_dialog import LoadingDialog +from .log_handler import QObjectLogHandler, get_gui_log_handler from .log_viewer_dialog import LogViewerDialog from .model_selection_panel import ModelSelectionPanel from .overlay_effects import OverlayWidget @@ -45,5 +47,7 @@ "ModelSelectionPanel", "MultiColumnComboBox", "OverlayWidget", + "QObjectLogHandler", "ToggleSwitch", + "get_gui_log_handler", ] diff --git a/src/voxkit/gui/components/animate_stack.py b/src/voxkit/gui/components/animate_stack.py index 3be16c2..d7c8592 100644 --- a/src/voxkit/gui/components/animate_stack.py +++ b/src/voxkit/gui/components/animate_stack.py @@ -1,11 +1,4 @@ -"""Animated Stack Module. - -QStackedWidget with smooth slide transition animations. - -API ---- -- **AnimatedStackedWidget**: Stacked widget with animated page transitions -""" +"""QStackedWidget with smooth slide transition animations.""" from PyQt6.QtCore import QEasingCurve, QPoint, QPropertyAnimation from PyQt6.QtWidgets import QStackedWidget diff --git a/src/voxkit/gui/components/column_dropdown.py b/src/voxkit/gui/components/column_dropdown.py index 3967cec..f7d4f77 100644 --- a/src/voxkit/gui/components/column_dropdown.py +++ b/src/voxkit/gui/components/column_dropdown.py @@ -1,11 +1,4 @@ -"""Column Dropdown Module. - -QComboBox with multi-column table dropdown display. - -API ---- -- **MultiColumnComboBox**: ComboBox with tabular dropdown (single selection) -""" +"""QComboBox with multi-column table dropdown display.""" import sys diff --git a/src/voxkit/gui/components/csv_viewer_dialog.py b/src/voxkit/gui/components/csv_viewer_dialog.py index e193491..fdf83cf 100644 --- a/src/voxkit/gui/components/csv_viewer_dialog.py +++ b/src/voxkit/gui/components/csv_viewer_dialog.py @@ -1,11 +1,4 @@ -"""CSV Viewer Dialog Module. - -Modal dialog for viewing CSV files in a formatted table. - -API ---- -- **CSVViewerDialog**: Dialog that displays CSV data with blur background effect -""" +"""Modal dialog for viewing CSV files in a formatted table.""" import csv import os diff --git a/src/voxkit/gui/components/dna_strand.py b/src/voxkit/gui/components/dna_strand.py index 83fc243..c47f36c 100644 --- a/src/voxkit/gui/components/dna_strand.py +++ b/src/voxkit/gui/components/dna_strand.py @@ -1,11 +1,4 @@ -"""DNA Strand Widget Module. - -Decorative audio waveform visualization for the toolbar. - -API ---- -- **DNAStrandWidget**: Procedural audio waveform decoration (Wav2Vec homage) -""" +"""Decorative audio waveform visualization for the toolbar.""" import math diff --git a/src/voxkit/gui/components/grip_splitter.py b/src/voxkit/gui/components/grip_splitter.py index 7470099..065a516 100644 --- a/src/voxkit/gui/components/grip_splitter.py +++ b/src/voxkit/gui/components/grip_splitter.py @@ -1,12 +1,4 @@ -"""Grip Splitter Module. - -Custom QSplitter with a visible grip handle for intuitive resizing. - -API ---- -- **GripSplitter**: QSplitter with custom grip handles -- **GripSplitterHandle**: Custom handle with visible grip dots -""" +"""Custom QSplitter with a visible grip handle for intuitive resizing.""" from PyQt6.QtCore import Qt from PyQt6.QtGui import QColor, QPainter diff --git a/src/voxkit/gui/components/huggingface_button.py b/src/voxkit/gui/components/huggingface_button.py index 665cc62..2a21e2b 100644 --- a/src/voxkit/gui/components/huggingface_button.py +++ b/src/voxkit/gui/components/huggingface_button.py @@ -1,11 +1,4 @@ -"""HuggingFace Button Module. - -Branded button with HuggingFace logo for model hub integration. - -API ---- -- **HuggingFaceButton**: QPushButton with HuggingFace emoji and styling -""" +"""Branded button with HuggingFace logo for model hub integration.""" from PyQt6.QtCore import Qt from PyQt6.QtWidgets import QHBoxLayout, QLabel, QPushButton, QWidget diff --git a/src/voxkit/gui/components/loading_dialog.py b/src/voxkit/gui/components/loading_dialog.py index 87e1c61..0c08875 100644 --- a/src/voxkit/gui/components/loading_dialog.py +++ b/src/voxkit/gui/components/loading_dialog.py @@ -1,11 +1,4 @@ -"""Loading Dialog Module. - -Splash screen / loading dialog for long-running operations. - -API ---- -- **LoadingDialog**: Modal dialog with animated spinner and status message -""" +"""Splash screen / loading dialog for long-running operations.""" from PyQt6.QtCore import QEasingCurve, QPropertyAnimation, Qt, QTimer from PyQt6.QtWidgets import QDialog, QGraphicsOpacityEffect, QLabel, QVBoxLayout diff --git a/src/voxkit/gui/components/log_handler.py b/src/voxkit/gui/components/log_handler.py index 1ee5eac..6b3cfb1 100644 --- a/src/voxkit/gui/components/log_handler.py +++ b/src/voxkit/gui/components/log_handler.py @@ -1,8 +1,5 @@ -"""Qt-aware logging handler that emits records as a pyqtSignal. - -Bridges stdlib :mod:`logging` into Qt's signal/slot system so dialogs and -widgets can subscribe to live log output without polling the log file. -""" +"""Bridges stdlib :mod:`logging` into Qt's signal/slot system so dialogs and +widgets can subscribe to live log output without polling the log file.""" import logging diff --git a/src/voxkit/gui/components/log_viewer_dialog.py b/src/voxkit/gui/components/log_viewer_dialog.py index 0aafe48..1f253ce 100644 --- a/src/voxkit/gui/components/log_viewer_dialog.py +++ b/src/voxkit/gui/components/log_viewer_dialog.py @@ -1,10 +1,7 @@ -"""Log Viewer Dialog. - -Read-only dialog that shows the tail of the rolling log file and appends +"""Read-only dialog that shows the tail of the rolling log file and appends new records live via :class:`QObjectLogHandler`. Designed to be reusable from other surfaces (Dataset page, per-process views) by swapping the -source file. -""" +source file.""" from collections import deque from pathlib import Path diff --git a/src/voxkit/gui/components/model_selection_panel.py b/src/voxkit/gui/components/model_selection_panel.py index 034de41..d6ffc2d 100644 --- a/src/voxkit/gui/components/model_selection_panel.py +++ b/src/voxkit/gui/components/model_selection_panel.py @@ -1,11 +1,4 @@ -"""Model Selection Panel Module. - -Combined engine and model selection widget for pipeline pages. - -API ---- -- **ModelSelectionPanel**: GroupBox with engine radio buttons and model dropdowns -""" +"""Combined engine and model selection widget for pipeline pages.""" from PyQt6.QtWidgets import ( QButtonGroup, diff --git a/src/voxkit/gui/components/overlay_effects.py b/src/voxkit/gui/components/overlay_effects.py index c13b797..61c4972 100644 --- a/src/voxkit/gui/components/overlay_effects.py +++ b/src/voxkit/gui/components/overlay_effects.py @@ -1,11 +1,4 @@ -"""Overlay Effects Module. - -Semi-transparent overlay widget for modal dimming effects. - -API ---- -- **OverlayWidget**: Translucent overlay for background dimming -""" +"""Semi-transparent overlay widget for modal dimming effects.""" from PyQt6.QtCore import Qt from PyQt6.QtGui import QColor, QPainter diff --git a/src/voxkit/gui/components/toggle_switch.py b/src/voxkit/gui/components/toggle_switch.py index 7c5947a..a542b62 100644 --- a/src/voxkit/gui/components/toggle_switch.py +++ b/src/voxkit/gui/components/toggle_switch.py @@ -1,11 +1,4 @@ -"""Toggle Switch Module. - -Animated iOS-style toggle switch widget. - -API ---- -- **ToggleSwitch**: Custom toggle switch with smooth animation -""" +"""Animated iOS-style toggle switch widget.""" from PyQt6.QtCore import ( # type: ignore[attr-defined] QEasingCurve, diff --git a/src/voxkit/storage/__init__.py b/src/voxkit/storage/__init__.py index 7d7b105..aa5eaee 100644 --- a/src/voxkit/storage/__init__.py +++ b/src/voxkit/storage/__init__.py @@ -1,6 +1,4 @@ -"""VoxKit Storage Module. - -Persistence and CRUD operations for datasets, models, and alignments. +"""Persistence and CRUD operations for voxkit assets which belong in local storage per user. Submodules ---------- @@ -11,7 +9,6 @@ Storage Structure ----------------- -:: ~/.voxkit/ ├── datasets/{dataset_id}/ @@ -25,18 +22,12 @@ Notes ----- - IDs are unique timestamps (YYYYMMDD_HHMMSS_ffffff) -- Storage root is created on first access +- Storage root is created on first access, or in the startup routine - Failed operations clean up partial changes """ -__author__ = "Beckett Frey" -__email__ = "beckett.frey@gmail.com" -__version__ = "0.0.1" - - # Import utils but don't call get_storage_root() at module import time from . import alignments, datasets, models, utils -from .alignments import HAND_ALIGNMENT_SENTINEL, AlignmentStatus def _ensure_storage_root(): @@ -62,11 +53,4 @@ def _ensure_storage_root(): _ensure_storage_root() -__all__ = [ - "alignments", - "datasets", - "models", - "utils", - "AlignmentStatus", - "HAND_ALIGNMENT_SENTINEL", -] +__all__ = ["alignments", "datasets", "models", "utils"] diff --git a/src/voxkit/storage/alignments.py b/src/voxkit/storage/alignments.py index fba881e..6ca093e 100644 --- a/src/voxkit/storage/alignments.py +++ b/src/voxkit/storage/alignments.py @@ -1,6 +1,4 @@ -"""Dataset Alignment Storage Module. - -Specialized CRUD operations for managing dataset alignments within the VoxKit storage system. +"""Specialized CRUD operations for managing dataset alignments within the VoxKit storage system. Directory Structure ------------------- diff --git a/src/voxkit/storage/config.py b/src/voxkit/storage/config.py index f3def3c..9f3312f 100644 --- a/src/voxkit/storage/config.py +++ b/src/voxkit/storage/config.py @@ -1,6 +1,4 @@ -"""Storage Configuration. - -This module contains configuration constants for the VoxKit storage system. +"""This module contains constants for the VoxKit storage system. Constants --------- diff --git a/src/voxkit/storage/datasets.py b/src/voxkit/storage/datasets.py index 47f2133..1e9d2bc 100644 --- a/src/voxkit/storage/datasets.py +++ b/src/voxkit/storage/datasets.py @@ -1,6 +1,4 @@ -"""Dataset Management Module. - -Specialized CRUD operations for managing datasets within the VoxKit storage system. +"""Specialized CRUD operations for managing datasets within the VoxKit storage system. Directory Structure ------------------- diff --git a/src/voxkit/storage/models.py b/src/voxkit/storage/models.py index 6aa9b58..52e5f7b 100644 --- a/src/voxkit/storage/models.py +++ b/src/voxkit/storage/models.py @@ -1,6 +1,4 @@ -"""Model Management Module. - -Specialized CRUD operations for managing models within the VoxKit storage system. +"""Specialized CRUD operations for managing models within the VoxKit storage system. Directory Structure ------------------- diff --git a/src/voxkit/storage/utils.py b/src/voxkit/storage/utils.py index c0460e6..7cb4588 100644 --- a/src/voxkit/storage/utils.py +++ b/src/voxkit/storage/utils.py @@ -1,6 +1,4 @@ -"""Utility Module for VoxKit Storage. - -This module provides utility functions for common VoxKit storage operations, +"""This module provides utility functions for common VoxKit storage operations, including path management and unique identifier generation. API