Skip to content

Commit 4c70b5b

Browse files
authored
[GuideLLM Refactor] utility package updates, rewrites, and tests expansion needed for the other, refactored packages (#353)
## **Summary** Introduces a comprehensive utilities infrastructure to support distributed processing, inter-process communication, and statistical analysis for the GuideLLM framework. The changes include new modules for encoding/serialization, messaging systems, statistical computations, and various utility mixins while removing deprecated functionality and improving code organization. ## **Details** - **Added messaging infrastructure** (`messaging.py`): Inter-process communication abstractions supporting queue-based, pipe-based, and manager-based messaging with configurable encoding and serialization - **Added encoding utilities** (`encoding.py`): High-performance message encoding/decoding with Pydantic model support, configurable serialization strategies (dict/sequence), and binary encoding (msgpack/msgspec) - **Added statistical analysis** (`statistics.py`): Comprehensive statistical computation tools including distribution summaries, percentiles, running statistics, and specialized request timing analysis - **Added registry system** (`registry.py`): Dynamic object registration and discovery with auto-discovery capabilities for extensible plugin architectures - **Added Pydantic utilities** (`pydantic_utils.py`): Polymorphic model serialization, registry integration, and standardized base model classes - **Added console utilities** (`console.py`): Rich console integration with status tracking, colored output, and progress indicators - **Added synchronization utilities** (`synchronous.py`): Async-compatible wrappers for threading/multiprocessing synchronization primitives - **Added singleton patterns** (`singleton.py`): Thread-safe and basic singleton implementations for resource management - **Added utility functions** (`functions.py`): Safe arithmetic operations, timestamp formatting, and defensive programming utilities - **Added mixin classes** (`mixins.py`): Reusable mixins for metadata extraction and object introspection - **Added auto-importer** (`auto_importer.py`): Automatic module importing for dynamic class discovery - **Enhanced text utilities**: Added `format_value_display()` function for consistent metric formatting and improved documentation - **Removed deprecated code**: Deleted `dict.py` module with `recursive_key_update()` and `camelize_str()` function from `text.py` - **Updated imports**: Comprehensive reorganization of `__init__.py` exports to reflect new utilities structure ## **Test Plan** - Full unit tests added and passing ## **Related Issues** This refactor supports the broader scheduler infrastructure improvements and distributed processing capabilities. --- - [x] "I certify that all code in this PR is my own, except as noted below." ## **Use of AI** - [x] Includes AI-assisted code completion - [x] Includes code generated by an AI application - [ ] Includes AI-generated tests (NOTE: AI written tests should have a docstring that includes `## WRITTEN BY AI ##`)
2 parents 3f1ff7a + 5b83c2d commit 4c70b5b

30 files changed

+9498
-250
lines changed

src/guidellm/benchmark/progress.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ def format_progress_display(
253253
decimal_places: Optional[int] = None,
254254
) -> str:
255255
if decimal_places is None and digits_places is None:
256-
formatted_number = f"{value}:.0f"
256+
formatted_number = f"{value:.0f}"
257257
elif digits_places is None:
258258
formatted_number = f"{value:.{decimal_places}f}"
259259
elif decimal_places is None:

src/guidellm/objects/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from .pydantic import StandardBaseModel, StatusBreakdown
21
from .statistics import (
32
DistributionSummary,
43
Percentiles,

src/guidellm/objects/pydantic.py

Lines changed: 0 additions & 89 deletions
This file was deleted.

src/guidellm/utils/__init__.py

Lines changed: 93 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,125 @@
1-
from .colors import Colors
1+
from .auto_importer import AutoImporterMixin
2+
from .console import Colors, Console, ConsoleUpdateStep, StatusIcons, StatusStyles
23
from .default_group import DefaultGroupHandler
3-
from .dict import recursive_key_update
4+
from .encoding import (
5+
Encoder,
6+
EncodingTypesAlias,
7+
MessageEncoding,
8+
SerializationTypesAlias,
9+
Serializer,
10+
)
11+
from .functions import (
12+
all_defined,
13+
safe_add,
14+
safe_divide,
15+
safe_format_timestamp,
16+
safe_getattr,
17+
safe_multiply,
18+
)
419
from .hf_datasets import (
520
SUPPORTED_TYPES,
621
save_dataset_to_file,
722
)
823
from .hf_transformers import (
924
check_load_processor,
1025
)
26+
from .messaging import (
27+
InterProcessMessaging,
28+
InterProcessMessagingManagerQueue,
29+
InterProcessMessagingPipe,
30+
InterProcessMessagingQueue,
31+
SendMessageT,
32+
)
33+
from .mixins import InfoMixin
34+
from .pydantic_utils import (
35+
PydanticClassRegistryMixin,
36+
ReloadableBaseModel,
37+
StandardBaseDict,
38+
StandardBaseModel,
39+
StatusBreakdown,
40+
)
1141
from .random import IntegerRangeSampler
42+
from .registry import RegistryMixin, RegistryObjT
43+
from .singleton import SingletonMixin, ThreadSafeSingletonMixin
44+
from .statistics import (
45+
DistributionSummary,
46+
Percentiles,
47+
RunningStats,
48+
StatusDistributionSummary,
49+
TimeRunningStats,
50+
)
51+
from .synchronous import (
52+
wait_for_sync_barrier,
53+
wait_for_sync_event,
54+
wait_for_sync_objects,
55+
)
1256
from .text import (
1357
EndlessTextCreator,
14-
camelize_str,
1558
clean_text,
1659
filter_text,
17-
is_puncutation,
60+
format_value_display,
61+
is_punctuation,
1862
load_text,
1963
split_text,
2064
split_text_list_by_length,
2165
)
66+
from .typing import get_literal_vals
2267

2368
__all__ = [
2469
"SUPPORTED_TYPES",
70+
"AutoImporterMixin",
71+
"Colors",
2572
"Colors",
73+
"Console",
74+
"ConsoleUpdateStep",
2675
"DefaultGroupHandler",
76+
"DistributionSummary",
77+
"Encoder",
78+
"EncodingTypesAlias",
2779
"EndlessTextCreator",
80+
"InfoMixin",
2881
"IntegerRangeSampler",
29-
"camelize_str",
82+
"InterProcessMessaging",
83+
"InterProcessMessagingManagerQueue",
84+
"InterProcessMessagingPipe",
85+
"InterProcessMessagingQueue",
86+
"MessageEncoding",
87+
"MessageEncoding",
88+
"Percentiles",
89+
"PydanticClassRegistryMixin",
90+
"RegistryMixin",
91+
"RegistryObjT",
92+
"ReloadableBaseModel",
93+
"RunningStats",
94+
"SendMessageT",
95+
"SerializationTypesAlias",
96+
"Serializer",
97+
"SingletonMixin",
98+
"StandardBaseDict",
99+
"StandardBaseModel",
100+
"StatusBreakdown",
101+
"StatusDistributionSummary",
102+
"StatusIcons",
103+
"StatusStyles",
104+
"ThreadSafeSingletonMixin",
105+
"TimeRunningStats",
106+
"all_defined",
30107
"check_load_processor",
31108
"clean_text",
32109
"filter_text",
33-
"is_puncutation",
110+
"format_value_display",
111+
"get_literal_vals",
112+
"is_punctuation",
34113
"load_text",
35-
"recursive_key_update",
114+
"safe_add",
115+
"safe_divide",
116+
"safe_format_timestamp",
117+
"safe_getattr",
118+
"safe_multiply",
36119
"save_dataset_to_file",
37120
"split_text",
38121
"split_text_list_by_length",
122+
"wait_for_sync_barrier",
123+
"wait_for_sync_event",
124+
"wait_for_sync_objects",
39125
]
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
"""
2+
Automatic module importing utilities for dynamic class discovery.
3+
4+
This module provides a mixin class for automatic module importing within a package,
5+
enabling dynamic discovery of classes and implementations without explicit imports.
6+
It is particularly useful for auto-registering classes in a registry pattern where
7+
subclasses need to be discoverable at runtime.
8+
9+
The AutoImporterMixin can be combined with registration mechanisms to create
10+
extensible systems where new implementations are automatically discovered and
11+
registered when they are placed in the correct package structure.
12+
"""
13+
14+
from __future__ import annotations
15+
16+
import importlib
17+
import pkgutil
18+
import sys
19+
from typing import ClassVar
20+
21+
__all__ = ["AutoImporterMixin"]
22+
23+
24+
class AutoImporterMixin:
25+
"""
26+
Mixin class for automatic module importing within packages.
27+
28+
This mixin enables dynamic discovery of classes and implementations without
29+
explicit imports by automatically importing all modules within specified
30+
packages. It is designed for use with class registration mechanisms to enable
31+
automatic discovery and registration of classes when they are placed in the
32+
correct package structure.
33+
34+
Example:
35+
::
36+
from guidellm.utils import AutoImporterMixin
37+
38+
class MyRegistry(AutoImporterMixin):
39+
auto_package = "my_package.implementations"
40+
41+
MyRegistry.auto_import_package_modules()
42+
43+
:cvar auto_package: Package name or tuple of package names to import modules from
44+
:cvar auto_ignore_modules: Module names to ignore during import
45+
:cvar auto_imported_modules: List tracking which modules have been imported
46+
"""
47+
48+
auto_package: ClassVar[str | tuple[str, ...] | None] = None
49+
auto_ignore_modules: ClassVar[tuple[str, ...] | None] = None
50+
auto_imported_modules: ClassVar[list[str] | None] = None
51+
52+
@classmethod
53+
def auto_import_package_modules(cls) -> None:
54+
"""
55+
Automatically import all modules within the specified package(s).
56+
57+
Scans the package(s) defined in the `auto_package` class variable and imports
58+
all modules found, tracking them in `auto_imported_modules`. Skips packages
59+
(directories) and any modules listed in `auto_ignore_modules`.
60+
61+
:raises ValueError: If the `auto_package` class variable is not set
62+
"""
63+
if cls.auto_package is None:
64+
raise ValueError(
65+
"The class variable 'auto_package' must be set to the package name to "
66+
"import modules from."
67+
)
68+
69+
cls.auto_imported_modules = []
70+
packages = (
71+
cls.auto_package
72+
if isinstance(cls.auto_package, tuple)
73+
else (cls.auto_package,)
74+
)
75+
76+
for package_name in packages:
77+
package = importlib.import_module(package_name)
78+
79+
for _, module_name, is_pkg in pkgutil.walk_packages(
80+
package.__path__, package.__name__ + "."
81+
):
82+
if (
83+
is_pkg
84+
or (
85+
cls.auto_ignore_modules is not None
86+
and module_name in cls.auto_ignore_modules
87+
)
88+
or module_name in cls.auto_imported_modules
89+
):
90+
# Skip packages and ignored modules
91+
continue
92+
93+
if module_name in sys.modules:
94+
# Avoid circular imports
95+
cls.auto_imported_modules.append(module_name)
96+
else:
97+
importlib.import_module(module_name)
98+
cls.auto_imported_modules.append(module_name)

src/guidellm/utils/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def __init__(self, *types: click.ParamType):
3535
self.types = types
3636
self.name = "".join(t.name for t in types)
3737

38-
def convert(self, value, param, ctx): # noqa: RET503
38+
def convert(self, value, param, ctx):
3939
fails = []
4040
for t in self.types:
4141
try:

0 commit comments

Comments
 (0)