Skip to content

Commit fa97e20

Browse files
author
Dmytro Parfeniuk
committed
Backend unit tests are added
* base unit tests are added * OpenAI unit tests are added Other changes: * mypy is configured in pyproject.toml * Makefile is improved * `polyfactory` package is INSTALLED for creating mock data models
1 parent 4af96de commit fa97e20

File tree

19 files changed

+434
-150
lines changed

19 files changed

+434
-150
lines changed

Makefile

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
install:
33
python -m pip install -r requirements.txt
44

5+
56
.PHONY: install.dev
67
install.dev:
78
python -m pip install -e .[dev]
89

10+
911
.PHONY: build
1012
build:
1113
python setup.py sdist bdist_wheel
@@ -15,8 +17,7 @@ build:
1517
quality:
1618
python -m ruff check src tests
1719
python -m isort --check src tests
18-
python -m flake8 src tests --max-line-length 88
19-
python -m mypy src
20+
python -m mypy
2021

2122

2223
.PHONY: style
@@ -28,16 +29,19 @@ style:
2829

2930
.PHONY: test
3031
test:
31-
python -m pytest -s -vvv --cache-clear tests
32+
python -m pytest tests
33+
3234

3335
.PHONY: test.unit
3436
test.unit:
3537
python -m pytest tests/unit
3638

39+
3740
.PHONY: test.integration
3841
test.integration:
3942
python -m pytest tests/integration
4043

44+
4145
.PHONY: test.e2e
4246
test.e2e:
4347
python -m pytest tests/e2e

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
TODO
1+
TODO

pyproject.toml

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,60 @@
11
[build-system]
2-
requires = ["setuptools", "wheel"]
3-
build-backend = "setuptools.build_meta"
2+
requires = ['setuptools', 'wheel']
3+
build-backend = 'setuptools.build_meta'
4+
5+
46

57
[tool.black]
68
line-length = 88
79
target-version = ['py38']
810

11+
12+
913
[tool.isort]
10-
profile = "black"
14+
profile = 'black'
15+
1116

12-
[tool.mypy]
13-
files = "src/guidellm"
1417

1518
[tool.ruff]
16-
exclude = ["build", "dist", "env", ".venv"]
19+
exclude = ['build', 'dist', 'env', '.venv']
1720
lint.select = ["E", "F", "W"]
21+
line-length = 88
22+
1823

19-
[tool.flake8]
20-
max-line-length = 88
2124

2225
[tool.pytest.ini_options]
2326
addopts = '-s -vvv --cache-clear'
2427
asyncio_mode = 'auto'
2528
markers = [
26-
"smoke: quick tests to check basic functionality",
27-
"sanity: detailed tests to ensure major functions work correctly",
28-
"regression: tests to ensure that new changes do not break existing functionality"
29+
'smoke: quick tests to check basic functionality',
30+
'sanity: detailed tests to ensure major functions work correctly',
31+
'regression: tests to ensure that new changes do not break existing functionality'
32+
]
33+
filterwarnings = [
34+
'ignore::RuntimeWarning',
35+
'ignore::UserWarning',
36+
'ignore::DeprecationWarning',
2937
]
3038

39+
40+
41+
[tool.mypy]
42+
python_version = '3.8'
43+
files = 'src/guidellm'
44+
show_error_codes = true
45+
namespace_packages = false
46+
check_untyped_defs = true
47+
48+
warn_redundant_casts = true
49+
warn_unused_ignores = true
50+
51+
# Silint "type import errors" as our 3rd-party libs does not have types
52+
# Check: https://mypy.readthedocs.io/en/latest/config_file.html#import-discovery
53+
follow_imports = 'silent'
54+
55+
[[tool.mypy.overrides]]
56+
module = ['transformers.*']
57+
ignore_missing_imports=true
58+
59+
60+

setup.py

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,52 @@
1-
from setuptools import setup, find_packages
21
from typing import Tuple
32

3+
from setuptools import find_packages, setup
4+
45

56
def _setup_long_description() -> Tuple[str, str]:
67
return open("README.md", "r", encoding="utf-8").read(), "text/markdown"
78

89

910
setup(
10-
name='guidellm',
11-
version='0.1.0',
12-
author='Neuralmagic, Inc.',
13-
description='Guidance platform for deploying and managing large language models.',
11+
name="guidellm",
12+
version="0.1.0",
13+
author="Neuralmagic, Inc.",
14+
description="Guidance platform for deploying and managing large language models.",
1415
long_description=_setup_long_description()[0],
1516
long_description_content_type=_setup_long_description()[1],
1617
license="Apache",
1718
url="https://github.com/neuralmagic/guidellm",
18-
packages=find_packages(where='src'),
19-
package_dir={'': 'src'},
19+
packages=find_packages(where="src"),
20+
package_dir={"": "src"},
2021
include_package_data=True,
2122
install_requires=[
22-
'click',
23-
'datasets',
24-
'loguru',
25-
'numpy',
26-
'openai',
27-
'requests',
28-
'transformers',
23+
"click",
24+
"datasets",
25+
"loguru",
26+
"numpy",
27+
"openai",
28+
"requests",
29+
"transformers",
2930
],
3031
extras_require={
31-
'dev': [
32-
'pytest',
33-
'sphinx',
34-
'ruff',
35-
'mypy',
36-
'black',
37-
'isort',
38-
'flake8',
39-
'pre-commit',
32+
"dev": [
33+
"black",
34+
"flake8",
35+
"isort",
36+
"mypy",
37+
"polyfactory",
38+
"pre-commit",
39+
"pytest",
40+
"ruff",
41+
"sphinx",
4042
],
4143
},
4244
entry_points={
43-
'console_scripts': [
44-
'guidellm=guidellm.main:main',
45+
"console_scripts": [
46+
"guidellm=guidellm.main:main",
4547
],
4648
},
47-
python_requires=">=3.8.0",
49+
python_requires=">=3.8.0,<4.0",
4850
classifiers=[
4951
"Development Status :: 5 - Production/Stable",
5052
"Programming Language :: Python :: 3",

src/__init__.py

Whitespace-only changes.

src/guidellm/backend/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
from .base import Backend, BackendTypes, GenerativeResponse
1+
from .base import Backend, BackendEngine, GenerativeResponse
22
from .openai import OpenAIBackend
33

44
__all__ = [
55
"Backend",
6-
"BackendTypes",
6+
"BackendEngine",
77
"GenerativeResponse",
88
"OpenAIBackend",
99
]

src/guidellm/backend/base.py

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
1-
import uuid
21
from abc import ABC, abstractmethod
32
from dataclasses import dataclass
43
from enum import Enum
54
from typing import Iterator, List, Optional, Type, Union
65

76
from loguru import logger
87

9-
from guidellm.core.request import TextGenerationRequest
10-
from guidellm.core.result import TextGenerationResult
8+
from guidellm.core import TextGenerationRequest, TextGenerationResult
119

12-
__all__ = ["Backend", "BackendTypes", "GenerativeResponse"]
10+
__all__ = ["Backend", "BackendEngine", "GenerativeResponse"]
1311

1412

15-
class BackendTypes(Enum):
13+
class BackendEngine(str, Enum):
14+
"""
15+
Determines the Engine of the LLM Backend.
16+
All the implemented backends in the project have the engine.
17+
18+
NOTE: the `TEST` engine has to be used only for testing purposes.
19+
"""
20+
1621
TEST = "test"
1722
OPENAI_SERVER = "openai_server"
1823

@@ -33,43 +38,46 @@ class GenerativeResponse:
3338

3439
class Backend(ABC):
3540
"""
36-
An abstract base class for generative AI backends.
41+
An abstract base class with template methods for generative AI backends.
3742
"""
3843

3944
_registry = {}
4045

41-
@staticmethod
42-
def register_backend(backend_type: BackendTypes):
46+
@classmethod
47+
def register(cls, backend_type: BackendEngine):
4348
"""
4449
A decorator to register a backend class in the backend registry.
4550
4651
:param backend_type: The type of backend to register.
47-
:type backend_type: BackendTypes
52+
:type backend_type: BackendType
4853
"""
4954

5055
def inner_wrapper(wrapped_class: Type["Backend"]):
51-
Backend._registry[backend_type] = wrapped_class
56+
cls._registry[backend_type] = wrapped_class
5257
return wrapped_class
5358

5459
return inner_wrapper
5560

56-
@staticmethod
57-
def create_backend(backend_type: Union[str, BackendTypes], **kwargs) -> "Backend":
61+
@classmethod
62+
def create(cls, backend_type: Union[str, BackendEngine], **kwargs) -> "Backend":
5863
"""
5964
Factory method to create a backend based on the backend type.
6065
6166
:param backend_type: The type of backend to create.
62-
:type backend_type: BackendTypes
67+
:type backend_type: BackendType
6368
:param kwargs: Additional arguments for backend initialization.
6469
:type kwargs: dict
6570
:return: An instance of a subclass of Backend.
6671
:rtype: Backend
6772
"""
73+
6874
logger.info(f"Creating backend of type {backend_type}")
69-
if backend_type not in Backend._registry:
75+
76+
if backend_type not in cls._registry:
7077
logger.error(f"Unsupported backend type: {backend_type}")
7178
raise ValueError(f"Unsupported backend type: {backend_type}")
72-
return Backend._registry[backend_type](**kwargs)
79+
80+
return cls._registry[backend_type](**kwargs)
7381

7482
def submit(self, request: TextGenerationRequest) -> TextGenerationResult:
7583
"""
@@ -80,23 +88,25 @@ def submit(self, request: TextGenerationRequest) -> TextGenerationResult:
8088
:return: The populated result result.
8189
:rtype: TextGenerationResult
8290
"""
91+
8392
logger.info(f"Submitting request with prompt: {request.prompt}")
84-
result_id = str(uuid.uuid4())
85-
result = TextGenerationResult(result_id)
93+
result = TextGenerationResult(request=request)
8694
result.start(request.prompt)
8795

8896
for response in self.make_request(request):
8997
if response.type_ == "token_iter" and response.add_token:
9098
result.output_token(response.add_token)
9199
elif response.type_ == "final":
92100
result.end(
93-
response.output,
101+
# NOTE: clarify if the `or ""` makesa any sense
102+
response.output or "",
94103
response.prompt_token_count,
95104
response.output_token_count,
96105
)
97106
break
98107

99108
logger.info(f"Request completed with output: {result.output}")
109+
100110
return result
101111

102112
@abstractmethod
@@ -121,8 +131,10 @@ def available_models(self) -> List[str]:
121131
:return: A list of available models.
122132
:rtype: List[str]
123133
"""
124-
raise NotImplementedError()
125134

135+
pass
136+
137+
@property
126138
@abstractmethod
127139
def default_model(self) -> str:
128140
"""
@@ -131,7 +143,8 @@ def default_model(self) -> str:
131143
:return: The default model.
132144
:rtype: str
133145
"""
134-
raise NotImplementedError()
146+
147+
pass
135148

136149
@abstractmethod
137150
def model_tokenizer(self, model: str) -> Optional[str]:
@@ -143,4 +156,5 @@ def model_tokenizer(self, model: str) -> Optional[str]:
143156
:return: The tokenizer for the model, or None if it cannot be created.
144157
:rtype: Optional[str]
145158
"""
146-
raise NotImplementedError()
159+
160+
pass

0 commit comments

Comments
 (0)