Skip to content

Commit 2ea8e7d

Browse files
committed
Update GenerativeTextScenario to match current def
Replace scenario entrypoint with a decorator Forward-port get_default and from_file to Scenario Apply scenario args as an update to kwargs Readd scenario support to CLI Signed-off-by: Samuel Monson <[email protected]>
1 parent 6d0d4c2 commit 2ea8e7d

File tree

4 files changed

+184
-115
lines changed

4 files changed

+184
-115
lines changed

src/guidellm/__main__.py

Lines changed: 68 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from typing import Annotated, Union
3232

3333
import click
34+
from pydantic import ValidationError
3435

3536
try:
3637
import uvloop
@@ -55,6 +56,7 @@
5556
)
5657
from guidellm.benchmark.scenario import (
5758
GenerativeTextScenario,
59+
get_builtin_scenarios,
5860
)
5961
from guidellm.mock_server import MockServer, MockServerConfig
6062
from guidellm.preprocess.dataset import ShortPromptStrategy, process_dataset
@@ -134,6 +136,25 @@ def benchmark():
134136
help="Run a benchmark against a generative model using the specified arguments.",
135137
context_settings={"auto_envvar_prefix": "GUIDELLM"},
136138
)
139+
@click.option(
140+
"--scenario",
141+
type=cli_tools.Union(
142+
click.Path(
143+
exists=True,
144+
readable=True,
145+
file_okay=True,
146+
dir_okay=False,
147+
path_type=Path,
148+
),
149+
click.Choice(get_builtin_scenarios()),
150+
),
151+
default=None,
152+
help=(
153+
"The name of a builtin scenario or path to a config file. "
154+
"Missing values from the config will use defaults. "
155+
"Options specified on the commandline will override the scenario."
156+
),
157+
)
137158
@click.option(
138159
"--target",
139160
type=str,
@@ -160,7 +181,7 @@ def benchmark():
160181
)
161182
@click.option(
162183
"--rate",
163-
default=None,
184+
default=GenerativeTextScenario.get_default("rate"),
164185
help=(
165186
"The rates to run the benchmark at. "
166187
"Can be a single number or a comma-separated list of numbers. "
@@ -182,18 +203,18 @@ def benchmark():
182203
"--backend-type", # legacy alias
183204
"backend",
184205
type=click.Choice(list(get_literal_vals(BackendType))),
206+
default=GenerativeTextScenario.get_default("backend"),
185207
help=(
186208
"The type of backend to use to run requests against. Defaults to 'openai_http'."
187209
f" Supported types: {', '.join(get_literal_vals(BackendType))}"
188210
),
189-
default="openai_http",
190211
)
191212
@click.option(
192213
"--backend-kwargs",
193214
"--backend-args", # legacy alias
194215
"backend_kwargs",
195216
callback=cli_tools.parse_json,
196-
default=None,
217+
default=GenerativeTextScenario.get_default("backend_kwargs"),
197218
help=(
198219
"A JSON string containing any arguments to pass to the backend as a "
199220
"dict with **kwargs. Headers can be removed by setting their value to "
@@ -203,7 +224,7 @@ def benchmark():
203224
)
204225
@click.option(
205226
"--model",
206-
default=None,
227+
default=GenerativeTextScenario.get_default("model"),
207228
type=str,
208229
help=(
209230
"The ID of the model to benchmark within the backend. "
@@ -213,7 +234,7 @@ def benchmark():
213234
# Data configuration
214235
@click.option(
215236
"--processor",
216-
default=None,
237+
default=GenerativeTextScenario.get_default("processor"),
217238
type=str,
218239
help=(
219240
"The processor or tokenizer to use to calculate token counts for statistics "
@@ -223,7 +244,7 @@ def benchmark():
223244
)
224245
@click.option(
225246
"--processor-args",
226-
default=None,
247+
default=GenerativeTextScenario.get_default("processor_args"),
227248
callback=cli_tools.parse_json,
228249
help=(
229250
"A JSON string containing any arguments to pass to the processor constructor "
@@ -232,7 +253,7 @@ def benchmark():
232253
)
233254
@click.option(
234255
"--data-args",
235-
default=None,
256+
default=GenerativeTextScenario.get_default("data_args"),
236257
callback=cli_tools.parse_json,
237258
help=(
238259
"A JSON string containing any arguments to pass to the dataset creation "
@@ -241,7 +262,7 @@ def benchmark():
241262
)
242263
@click.option(
243264
"--data-sampler",
244-
default=None,
265+
default=GenerativeTextScenario.get_default("data_sampler"),
245266
type=click.Choice(["random"]),
246267
help=(
247268
"The data sampler type to use. 'random' will add a random shuffle on the data. "
@@ -300,7 +321,7 @@ def benchmark():
300321
"--warmup-percent", # legacy alias
301322
"warmup",
302323
type=float,
303-
default=None,
324+
default=GenerativeTextScenario.get_default("warmup"),
304325
help=(
305326
"The specification around the number of requests to run before benchmarking. "
306327
"If within (0, 1), then the percent of requests/time to use for warmup. "
@@ -314,7 +335,7 @@ def benchmark():
314335
"--cooldown-percent", # legacy alias
315336
"cooldown",
316337
type=float,
317-
default=GenerativeTextScenario.get_default("cooldown_percent"),
338+
default=GenerativeTextScenario.get_default("cooldown"),
318339
help=(
319340
"The specification around the number of requests to run after benchmarking. "
320341
"If within (0, 1), then the percent of requests/time to use for cooldown. "
@@ -327,19 +348,19 @@ def benchmark():
327348
"--request-samples",
328349
"--output-sampling", # legacy alias
329350
"request_samples",
351+
default=GenerativeTextScenario.get_default("request_samples"),
330352
type=int,
331353
help=(
332354
"The number of samples for each request status and each benchmark to save "
333355
"in the output file. If None (default), will save all samples. "
334356
"Defaults to 20."
335357
),
336-
default=20,
337358
)
338359
# Constraints configuration
339360
@click.option(
340361
"--max-seconds",
341362
type=float,
342-
default=None,
363+
default=GenerativeTextScenario.get_default("max_seconds"),
343364
help=(
344365
"The maximum number of seconds each benchmark can run for. "
345366
"If None, will run until max_requests or the data is exhausted."
@@ -348,7 +369,7 @@ def benchmark():
348369
@click.option(
349370
"--max-requests",
350371
type=int,
351-
default=None,
372+
default=GenerativeTextScenario.get_default("max_requests"),
352373
help=(
353374
"The maximum number of requests each benchmark can run for. "
354375
"If None, will run until max_seconds or the data is exhausted."
@@ -357,55 +378,22 @@ def benchmark():
357378
@click.option(
358379
"--max-errors",
359380
type=int,
360-
default=None,
381+
default=GenerativeTextScenario.get_default("max_errors"),
361382
help="Maximum number of errors allowed before stopping the benchmark",
362383
)
363384
@click.option(
364385
"--max-error-rate",
365386
type=float,
366-
default=None,
387+
default=GenerativeTextScenario.get_default("max_error_rate"),
367388
help="Maximum error rate allowed before stopping the benchmark",
368389
)
369390
@click.option(
370391
"--max-global-error-rate",
371392
type=float,
372-
default=None,
393+
default=GenerativeTextScenario.get_default("max_global_error_rate"),
373394
help="Maximum global error rate allowed across all benchmarks",
374395
)
375-
def run(
376-
target,
377-
data,
378-
profile,
379-
rate,
380-
random_seed,
381-
# Backend Configuration
382-
backend,
383-
backend_kwargs,
384-
model,
385-
# Data configuration
386-
processor,
387-
processor_args,
388-
data_args,
389-
data_sampler,
390-
# Output configuration
391-
output_path,
392-
output_formats,
393-
# Updates configuration
394-
disable_console_outputs,
395-
disable_progress,
396-
display_scheduler_stats,
397-
# Aggregators configuration
398-
output_extras,
399-
warmup,
400-
cooldown,
401-
request_samples,
402-
# Constraints configuration
403-
max_seconds,
404-
max_requests,
405-
max_errors,
406-
max_error_rate,
407-
max_global_error_rate,
408-
):
396+
def run(**kwargs):
409397
"""
410398
Execute a generative text benchmark against a target model backend.
411399
@@ -414,53 +402,53 @@ def run(
414402
Supports multiple backends, data sources, output formats, and constraint types
415403
for flexible benchmark configuration.
416404
"""
405+
scenario = kwargs.pop("scenario")
406+
click_ctx = click.get_current_context()
407+
overrides = cli_tools.set_if_not_default(click_ctx, **kwargs)
408+
409+
try:
410+
# If a scenario file was specified read from it
411+
if scenario is None:
412+
_scenario = GenerativeTextScenario.model_validate(overrides)
413+
elif isinstance(scenario, Path):
414+
_scenario = GenerativeTextScenario.from_file(scenario, overrides)
415+
else: # Only builtins can make it here; click will catch anything else
416+
_scenario = GenerativeTextScenario.from_builtin(scenario, overrides)
417+
except ValidationError as e:
418+
# Translate pydantic valdation error to click argument error
419+
errs = e.errors(include_url=False, include_context=True, include_input=True)
420+
param_name = "--" + str(errs[0]["loc"][0]).replace("_", "-")
421+
raise click.BadParameter(
422+
errs[0]["msg"], ctx=click_ctx, param_hint=param_name
423+
) from e
424+
417425
if HAS_UVLOOP:
418426
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
419427
asyncio.run(
420428
benchmark_generative_text(
421-
target=target,
422-
data=data,
423-
profile=profile,
424-
rate=rate,
425-
random_seed=random_seed,
426-
# Backend configuration
427-
backend=backend,
428-
backend_kwargs=backend_kwargs,
429-
model=model,
430-
# Data configuration
431-
processor=processor,
432-
processor_args=processor_args,
433-
data_args=data_args,
434-
data_sampler=data_sampler,
429+
scenario=_scenario,
435430
# Output configuration
436-
output_path=output_path,
431+
output_path=kwargs["output_path"],
437432
output_formats=[
438433
fmt
439-
for fmt in output_formats
440-
if not disable_console_outputs or fmt != "console"
434+
for fmt in kwargs["output_formats"]
435+
if not kwargs["disable_console_outputs"] or fmt != "console"
441436
],
442437
# Updates configuration
443438
progress=(
444439
[
445440
GenerativeConsoleBenchmarkerProgress(
446-
display_scheduler_stats=display_scheduler_stats
441+
display_scheduler_stats=kwargs["display_scheduler_stats"]
447442
)
448443
]
449-
if not disable_progress
444+
if not kwargs["disable_progress"]
450445
else None
451446
),
452-
print_updates=not disable_console_outputs,
447+
print_updates=not kwargs["disable_console_outputs"],
453448
# Aggregators configuration
454-
add_aggregators={"extras": InjectExtrasAggregator(extras=output_extras)},
455-
warmup=warmup,
456-
cooldown=cooldown,
457-
request_samples=request_samples,
458-
# Constraints configuration
459-
max_seconds=max_seconds,
460-
max_requests=max_requests,
461-
max_errors=max_errors,
462-
max_error_rate=max_error_rate,
463-
max_global_error_rate=max_global_error_rate,
449+
add_aggregators={
450+
"extras": InjectExtrasAggregator(extras=kwargs["output_extras"])
451+
},
464452
)
465453
)
466454

src/guidellm/benchmark/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@
4040
BenchmarkerProgressGroup,
4141
GenerativeConsoleBenchmarkerProgress,
4242
)
43+
from .scenario import (
44+
GenerativeTextScenario,
45+
Scenario,
46+
enable_scenarios,
47+
get_builtin_scenarios,
48+
)
4349

4450
__all__ = [
4551
"Aggregator",
@@ -65,14 +71,18 @@
6571
"GenerativeRequestStats",
6672
"GenerativeRequestsAggregator",
6773
"GenerativeStatsProgressAggregator",
74+
"GenerativeTextScenario",
6875
"InjectExtrasAggregator",
6976
"Profile",
7077
"ProfileType",
78+
"Scenario",
7179
"SchedulerStatsAggregator",
7280
"SerializableAggregator",
7381
"SweepProfile",
7482
"SynchronousProfile",
7583
"ThroughputProfile",
7684
"benchmark_generative_text",
85+
"enable_scenarios",
86+
"get_builtin_scenarios",
7787
"reimport_benchmarks_report",
7888
]

src/guidellm/benchmark/entrypoints.py

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
BenchmarkerProgress,
3535
BenchmarkerProgressGroup,
3636
)
37-
from guidellm.benchmark.scenario import GenerativeTextScenario, Scenario
37+
from guidellm.benchmark.scenario import enable_scenarios
3838
from guidellm.request import GenerativeRequestLoader
3939
from guidellm.scheduler import (
4040
ConstraintInitializer,
@@ -45,26 +45,15 @@
4545

4646
__all__ = [
4747
"benchmark_generative_text",
48-
"benchmark_with_scenario",
4948
"reimport_benchmarks_report",
5049
]
5150

5251

5352
_CURRENT_WORKING_DIR = Path.cwd()
5453

5554

56-
async def benchmark_with_scenario(scenario: Scenario, **kwargs):
57-
"""
58-
Run a benchmark using a scenario and specify any extra arguments
59-
"""
60-
61-
if isinstance(scenario, GenerativeTextScenario):
62-
return await benchmark_generative_text(**vars(scenario), **kwargs)
63-
else:
64-
raise ValueError(f"Unsupported Scenario type {type(scenario)}")
65-
66-
6755
# @validate_call(config={"arbitrary_types_allowed": True})
56+
@enable_scenarios
6857
async def benchmark_generative_text( # noqa: C901
6958
target: str,
7059
data: (

0 commit comments

Comments
 (0)