Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

➕ pytest-cov is installed. 75% threshhold is set #13

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ install.dev:
python -m pip install -e .[dev]



.PHONY: build
build:
python setup.py sdist bdist_wheel
Expand Down Expand Up @@ -35,7 +36,7 @@ quality:

.PHONY: test
test:
python -m pytest -s -vvv --cache-clear tests
python -m pytesttests


.PHONY: test.unit
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ code_quality = [
"isort",
"mypy",
"pytest",
"pytest-cov",
"pytest-mock",
"ruff",
"tox",
"types-requests"
"types-requests",
]


Expand Down Expand Up @@ -94,8 +95,7 @@ lint.select = ["E", "F", "W"]


[tool.pytest.ini_options]
addopts = '-s -vvv --cache-clear'
asyncio_mode = 'auto'
addopts = '-s -vvv --cache-clear --cov-report=term-missing --cov --cov-fail-under=75'
markers = [
"smoke: quick tests to check basic functionality",
"sanity: detailed tests to ensure major functions work correctly",
Expand Down
4 changes: 2 additions & 2 deletions src/guidellm/backend/openai.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import functools
import os
from typing import Any, Dict, Iterator, List, Optional
from typing import Any, Dict, Generator, List, Optional

from loguru import logger
from openai import OpenAI, Stream
Expand Down Expand Up @@ -72,7 +72,7 @@ def __init__(

def make_request(
self, request: TextGenerationRequest
) -> Iterator[GenerativeResponse]:
) -> Generator[GenerativeResponse, None, None]:
"""
Make a request to the OpenAI backend.

Expand Down
32 changes: 21 additions & 11 deletions src/guidellm/core/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def start_time(self) -> float:
:rtype: float
"""

self._recording_started()
self.check_recording_started()
assert self._start_time

return self._start_time
Expand All @@ -148,6 +148,7 @@ def end_time(self) -> float:
:rtype: float
"""

self.check_recording_started()
assert self._end_time
return self._end_time

Expand Down Expand Up @@ -192,7 +193,7 @@ def start(self, prompt: str):

logger.info(f"Text generation started with prompt: '{prompt}'")

def _recording_started(self, raise_exception: bool = True) -> bool:
def check_recording_started(self, raise_exception: bool = True) -> bool:
"""
Ensure that the benchmark text generation recording is started.

Expand All @@ -205,7 +206,7 @@ def _recording_started(self, raise_exception: bool = True) -> bool:
else:
if raise_exception is True:
raise ValueError(
"start time is not specified. "
"Start time is not specified. "
"Did you make the `text_generation_benchmark.start()`?"
)
else:
Expand Down Expand Up @@ -270,7 +271,11 @@ class TextGenerationError:
:type error: Exception
"""

def __init__(self, request: TextGenerationRequest, error: Exception):
def __init__(
self,
request: TextGenerationRequest,
error_class: BaseException,
):
"""
Initialize the TextGenerationError with a unique identifier.

Expand All @@ -279,10 +284,10 @@ def __init__(self, request: TextGenerationRequest, error: Exception):
:param error: The exception that occurred during the text generation.
:type error: Exception
"""
self._request = request
self._error = error
self._request: TextGenerationRequest = request
self._error_class: BaseException = error_class

logger.error(f"Error occurred for request: {self._request}: {error}")
logger.error(f"Error occurred for request: {self._request}: {error_class}")

def __repr__(self) -> str:
"""
Expand All @@ -291,7 +296,9 @@ def __repr__(self) -> str:
:return: String representation of the TextGenerationError.
:rtype: str
"""
return f"TextGenerationError(request={self._request}, error={self._error})"
return (
f"TextGenerationError(request={self._request}, error={self._error_class})"
)

@property
def request(self) -> TextGenerationRequest:
Expand All @@ -304,14 +311,14 @@ def request(self) -> TextGenerationRequest:
return self._request

@property
def error(self) -> Exception:
def error(self) -> BaseException:
"""
Get the exception that occurred during the text generation.

:return: The exception.
:rtype: Exception
"""
return self._error
return self._error_class


@dataclass
Expand Down Expand Up @@ -350,9 +357,11 @@ def __init__(self, mode: str, rate: Optional[float]):
self._results: List[TextGenerationResult] = []
self._errors: List[TextGenerationError] = []
self._concurrencies: List[RequestConcurrencyMeasurement] = []
self._overloaded = False
self._args_rate: Optional[float] = None

# NOTE: This state never changes
self._overloaded = False

logger.debug(
f"Initialized TextGenerationBenchmark with mode={mode} and rate={rate}"
)
Expand Down Expand Up @@ -413,6 +422,7 @@ def overloaded(self) -> bool:
:return: The overloaded state.
:rtype: bool
"""

return self._overloaded

@property
Expand Down
4 changes: 2 additions & 2 deletions src/guidellm/executor/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
from .executor import Executor
from .profile_generator import (
Profile,
ProfileGenerationModes,
ProfileGenerationMode,
ProfileGenerator,
SingleProfileGenerator,
SweepProfileGenerator,
)

__all__ = [
"Executor",
"ProfileGenerationModes",
"ProfileGenerationMode",
"Profile",
"ProfileGenerator",
"SingleProfileGenerator",
Expand Down
38 changes: 25 additions & 13 deletions src/guidellm/executor/executor.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,51 @@
from typing import Any, Dict, Optional, Union
from typing import Any, Dict, Optional

from guidellm.backend import Backend
from guidellm.core import TextGenerationBenchmarkReport
from guidellm.executor.profile_generator import ProfileGenerationModes, ProfileGenerator
from guidellm.core import TextGenerationBenchmark, TextGenerationBenchmarkReport
from guidellm.request import RequestGenerator
from guidellm.scheduler.scheduler import Scheduler
from guidellm.scheduler import Scheduler

from .profile_generator import ProfileGenerationMode, ProfileGenerator

__all__ = ["Executor"]


class Executor:
"""
The main purpose of the `class Executor` is to dispatch running tasks according
to the Profile Generation mode
"""

def __init__(
self,
request_generator: RequestGenerator,
backend: Backend,
profile_mode: Union[str, ProfileGenerationModes] = "single",
request_generator: RequestGenerator,
profile_mode: ProfileGenerationMode = ProfileGenerationMode.SINGLE,
profile_args: Optional[Dict[str, Any]] = None,
max_requests: Optional[int] = None,
max_duration: Optional[float] = None,
):
self.request_generator = request_generator
self.backend = backend
self.profile = ProfileGenerator.create_generator(
self.profile_generator: ProfileGenerator = ProfileGenerator.create(
profile_mode, **(profile_args or {})
)
self.max_requests = max_requests
self.max_duration = max_duration
self.max_requests: Optional[int] = max_requests
self.max_duration: Optional[float] = max_duration
self._scheduler: Optional[Scheduler] = None

@property
def scheduler(self) -> Scheduler:
if self._scheduler is None:
raise ValueError("The scheduler is not set. Did you run the execution?")
else:
return self._scheduler

def run(self) -> TextGenerationBenchmarkReport:
report = TextGenerationBenchmarkReport()

while True:
profile = self.profile.next_profile(report)

if profile is None:
if not (profile := self.profile_generator.next(report)):
break

scheduler = Scheduler(
Expand All @@ -45,7 +57,7 @@ def run(self) -> TextGenerationBenchmarkReport:
max_duration=self.max_duration,
)

benchmark = scheduler.run()
benchmark: TextGenerationBenchmark = scheduler.run()
report.add_benchmark(benchmark)

return report
Loading