Skip to content

Commit d57fe3d

Browse files
committed
Always parse rate as list[float]
Signed-off-by: Samuel Monson <[email protected]>
1 parent 4810568 commit d57fe3d

File tree

2 files changed

+38
-41
lines changed

2 files changed

+38
-41
lines changed

src/guidellm/benchmark/entrypoints.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ async def benchmark_generative_text( # noqa: C901
6767
| Path
6868
),
6969
profile: StrategyType | ProfileType | Profile,
70-
rate: float | list[float] | None = None,
70+
rate: list[float] | None = None,
7171
random_seed: int = 42,
7272
# Backend configuration
7373
backend: BackendType | Backend = "openai_http",

src/guidellm/benchmark/profile.py

Lines changed: 37 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,17 @@
2929
)
3030

3131
import numpy as np
32-
from pydantic import Field, computed_field, field_serializer, field_validator
32+
from pydantic import (
33+
Field,
34+
NonNegativeFloat,
35+
PositiveFloat,
36+
PositiveInt,
37+
computed_field,
38+
field_serializer,
39+
field_validator,
40+
)
3341

42+
from guidellm import settings
3443
from guidellm.scheduler import (
3544
AsyncConstantStrategy,
3645
AsyncPoissonStrategy,
@@ -86,7 +95,7 @@ def __pydantic_schema_base_type__(cls) -> type[Profile]:
8695
def create(
8796
cls,
8897
rate_type: str,
89-
rate: float | int | list[float | int] | None,
98+
rate: list[float] | None,
9099
random_seed: int = 42,
91100
**kwargs: Any,
92101
) -> Profile:
@@ -112,7 +121,7 @@ def create(
112121
def resolve_args(
113122
cls,
114123
rate_type: str,
115-
rate: float | int | list[float, int] | None,
124+
rate: list[float] | None,
116125
random_seed: int,
117126
**kwargs: Any,
118127
) -> dict[str, Any]:
@@ -265,7 +274,7 @@ class SynchronousProfile(Profile):
265274
def resolve_args(
266275
cls,
267276
rate_type: str,
268-
rate: float | int | list[float, int] | None,
277+
rate: list[float] | None,
269278
random_seed: int,
270279
**kwargs: Any,
271280
) -> dict[str, Any]:
@@ -316,24 +325,22 @@ class ConcurrentProfile(Profile):
316325
"""Fixed-concurrency strategy execution profile with configurable stream counts."""
317326

318327
type_: Literal["concurrent"] = "concurrent" # type: ignore[assignment]
319-
streams: int | list[int] = Field(
328+
streams: list[PositiveInt] = Field(
320329
description="Number of concurrent streams for request scheduling",
321-
gt=0,
322330
)
323-
startup_duration: float = Field(
331+
startup_duration: NonNegativeFloat = Field(
324332
default=0.0,
325333
description=(
326334
"Duration in seconds for distributing startup requests "
327335
"before completion-based timing"
328336
),
329-
ge=0,
330337
)
331338

332339
@classmethod
333340
def resolve_args(
334341
cls,
335342
rate_type: str,
336-
rate: float | int | list[float, int] | None,
343+
rate: list[float] | None,
337344
random_seed: int,
338345
**kwargs: Any,
339346
) -> dict[str, Any]:
@@ -348,14 +355,13 @@ def resolve_args(
348355
:raises ValueError: If rate is None.
349356
"""
350357
_ = (rate_type, random_seed) # unused
351-
kwargs["streams"] = rate
358+
kwargs["streams"] = [int(r) for r in rate] if rate else None
352359
return kwargs
353360

354361
@property
355362
def strategy_types(self) -> list[StrategyType]:
356363
"""Get concurrent strategy types for each configured stream count."""
357-
num_strategies = len(self.streams) if isinstance(self.streams, list) else 1
358-
return [self.type_] * num_strategies
364+
return [self.type_] * len(self.streams)
359365

360366
def next_strategy(
361367
self,
@@ -370,13 +376,12 @@ def next_strategy(
370376
:return: ConcurrentStrategy with next stream count, or None if complete.
371377
"""
372378
_ = (prev_strategy, prev_benchmark) # unused
373-
streams = self.streams if isinstance(self.streams, list) else [self.streams]
374379

375-
if len(self.completed_strategies) >= len(streams):
380+
if len(self.completed_strategies) >= len(self.streams):
376381
return None
377382

378383
return ConcurrentStrategy(
379-
streams=streams[len(self.completed_strategies)],
384+
streams=self.streams[len(self.completed_strategies)],
380385
startup_duration=self.startup_duration,
381386
)
382387

@@ -388,25 +393,22 @@ class ThroughputProfile(Profile):
388393
"""
389394

390395
type_: Literal["throughput"] = "throughput" # type: ignore[assignment]
391-
max_concurrency: int | None = Field(
396+
max_concurrency: PositiveInt | None = Field(
392397
default=None,
393398
description="Maximum number of concurrent requests to schedule",
394-
gt=0,
395399
)
396-
startup_duration: float = Field(
397-
default=0.0,
400+
startup_duration: NonNegativeFloat = Field(
398401
description=(
399402
"Duration in seconds for distributing startup requests "
400403
"before full throughput scheduling"
401404
),
402-
ge=0,
403405
)
404406

405407
@classmethod
406408
def resolve_args(
407409
cls,
408410
rate_type: str,
409-
rate: float | int | list[float, int] | None,
411+
rate: list[float] | None,
410412
random_seed: int,
411413
**kwargs: Any,
412414
) -> dict[str, Any]:
@@ -422,8 +424,8 @@ def resolve_args(
422424
_ = (rate_type, random_seed) # unused
423425
# Remap rate to max_concurrency, strip out random_seed
424426
kwargs.pop("random_seed", None)
425-
if rate is not None:
426-
kwargs["max_concurrency"] = rate
427+
if rate is not None and len(rate) > 0:
428+
kwargs["max_concurrency"] = rate[0]
427429
return kwargs
428430

429431
@property
@@ -463,22 +465,19 @@ class AsyncProfile(Profile):
463465
strategy_type: Literal["constant", "poisson"] = Field(
464466
description="Type of asynchronous strategy pattern to use",
465467
)
466-
rate: float | list[float] = Field(
468+
rate: list[PositiveFloat] = Field(
467469
description="Request scheduling rate in requests per second",
468-
gt=0,
469470
)
470-
startup_duration: float = Field(
471+
startup_duration: NonNegativeFloat = Field(
471472
default=0.0,
472473
description=(
473474
"Duration in seconds for distributing startup requests "
474475
"to converge quickly to desired rate"
475476
),
476-
ge=0,
477477
)
478-
max_concurrency: int | None = Field(
478+
max_concurrency: PositiveInt | None = Field(
479479
default=None,
480480
description="Maximum number of concurrent requests to schedule",
481-
gt=0,
482481
)
483482
random_seed: int = Field(
484483
default=42,
@@ -489,7 +488,7 @@ class AsyncProfile(Profile):
489488
def resolve_args(
490489
cls,
491490
rate_type: str,
492-
rate: float | int | list[float, int] | None,
491+
rate: list[float] | None,
493492
random_seed: int,
494493
**kwargs: Any,
495494
) -> dict[str, Any]:
@@ -523,7 +522,7 @@ def resolve_args(
523522
@property
524523
def strategy_types(self) -> list[StrategyType]:
525524
"""Get async strategy types for each configured rate."""
526-
num_strategies = len(self.rate) if isinstance(self.rate, list) else 1
525+
num_strategies = len(self.rate)
527526
return [self.strategy_type] * num_strategies
528527

529528
def next_strategy(
@@ -541,12 +540,11 @@ def next_strategy(
541540
:raises ValueError: If strategy_type is neither 'constant' nor 'poisson'.
542541
"""
543542
_ = (prev_strategy, prev_benchmark) # unused
544-
rate = self.rate if isinstance(self.rate, list) else [self.rate]
545543

546-
if len(self.completed_strategies) >= len(rate):
544+
if len(self.completed_strategies) >= len(self.rate):
547545
return None
548546

549-
current_rate = rate[len(self.completed_strategies)]
547+
current_rate = self.rate[len(self.completed_strategies)]
550548

551549
if self.strategy_type == "constant":
552550
return AsyncConstantStrategy(
@@ -577,18 +575,16 @@ class SweepProfile(Profile):
577575
ge=2,
578576
)
579577
strategy_type: Literal["constant", "poisson"] = "constant"
580-
startup_duration: float = Field(
578+
startup_duration: NonNegativeFloat = Field(
581579
default=0.0,
582580
description=(
583581
"Duration in seconds for distributing startup requests "
584582
"to converge quickly to desired rate"
585583
),
586-
ge=0,
587584
)
588-
max_concurrency: int | None = Field(
585+
max_concurrency: PositiveInt | None = Field(
589586
default=None,
590587
description="Maximum number of concurrent requests to schedule",
591-
gt=0,
592588
)
593589
random_seed: int = Field(
594590
default=42,
@@ -615,7 +611,7 @@ class SweepProfile(Profile):
615611
def resolve_args(
616612
cls,
617613
rate_type: str,
618-
rate: float | int | list[float, int] | None,
614+
rate: list[float] | None,
619615
random_seed: int,
620616
**kwargs: Any,
621617
) -> dict[str, Any]:
@@ -628,7 +624,8 @@ def resolve_args(
628624
:param kwargs: Additional arguments to pass through.
629625
:return: Dictionary of resolved arguments.
630626
"""
631-
kwargs["sweep_size"] = kwargs.get("sweep_size", rate)
627+
sweep_size_from_rate = int(rate[0]) if rate else settings.default_sweep_number
628+
kwargs["sweep_size"] = kwargs.get("sweep_size", sweep_size_from_rate)
632629
kwargs["random_seed"] = random_seed
633630
if rate_type in ["constant", "poisson"]:
634631
kwargs["strategy_type"] = rate_type

0 commit comments

Comments
 (0)