Skip to content

guidellm.benchmark.schemas

Benchmark schemas for performance measurement and result analysis.

This module consolidates the complete benchmark schema ecosystem, providing both base abstractions for benchmark execution and domain-specific implementations for generative AI tasks. It exports core configuration objects, accumulator interfaces for real-time metric collection, benchmark result containers with statistical summaries, and reporting utilities. The schemas support flexible scheduling strategies, comprehensive metric tracking including latency and throughput distributions, and multi-modal generative benchmarks for text, image, video, and audio generation tasks.

BenchmarkAccumulatorT = TypeVar('BenchmarkAccumulatorT', bound='BenchmarkAccumulator[Any, Any]') module-attribute

Generic type variable for benchmark accumulator implementations

BenchmarkT = TypeVar('BenchmarkT', bound='Benchmark') module-attribute

Generic type variable for benchmark result implementations

Benchmark

Bases: StandardBaseDict, ABC, Generic[BenchmarkAccumulatorT]

Compile and expose final benchmark execution metrics.

Defines the interface for benchmark result implementations capturing comprehensive performance metrics including latency distributions, throughput measurements, and concurrency patterns. Subclasses implement compilation logic to transform accumulated metrics and scheduler state into structured results with statistical summaries.

Source code in src/guidellm/benchmark/schemas/base.py
class Benchmark(StandardBaseDict, ABC, Generic[BenchmarkAccumulatorT]):
    """
    Compile and expose final benchmark execution metrics.

    Defines the interface for benchmark result implementations capturing
    comprehensive performance metrics including latency distributions, throughput
    measurements, and concurrency patterns. Subclasses implement compilation
    logic to transform accumulated metrics and scheduler state into structured
    results with statistical summaries.
    """

    @property
    @abstractmethod
    def start_time(self) -> float:
        """
        :return: Benchmark start timestamp in seconds since epoch
        """

    @property
    @abstractmethod
    def end_time(self) -> float:
        """
        :return: Benchmark completion timestamp in seconds since epoch
        """

    @property
    @abstractmethod
    def duration(self) -> float:
        """
        :return: Benchmark execution duration in seconds
        """

    @property
    @abstractmethod
    def request_latency(self) -> StatusDistributionSummary:
        """
        :return: Statistical distribution of request latencies
        """

    @property
    @abstractmethod
    def request_throughput(self) -> StatusDistributionSummary:
        """
        :return: Statistical distribution of throughput measurements
        """

    @property
    @abstractmethod
    def request_concurrency(self) -> StatusDistributionSummary:
        """
        :return: Statistical distribution of concurrent request counts
        """

    @classmethod
    @abstractmethod
    def compile(
        cls, accumulator: BenchmarkAccumulatorT, scheduler_state: SchedulerState
    ) -> Any:
        """
        Transform accumulated metrics into final benchmark results.

        :param accumulator: Accumulator instance with collected metrics and state
        :param scheduler_state: Scheduler's final state after execution completion
        :return: Compiled benchmark instance with complete statistical results
        """

duration abstractmethod property

Returns:

Type Description
float

Benchmark execution duration in seconds

end_time abstractmethod property

Returns:

Type Description
float

Benchmark completion timestamp in seconds since epoch

request_concurrency abstractmethod property

Returns:

Type Description
StatusDistributionSummary

Statistical distribution of concurrent request counts

request_latency abstractmethod property

Returns:

Type Description
StatusDistributionSummary

Statistical distribution of request latencies

request_throughput abstractmethod property

Returns:

Type Description
StatusDistributionSummary

Statistical distribution of throughput measurements

start_time abstractmethod property

Returns:

Type Description
float

Benchmark start timestamp in seconds since epoch

compile(accumulator, scheduler_state) abstractmethod classmethod

Transform accumulated metrics into final benchmark results.

Parameters:

Name Type Description Default
accumulator BenchmarkAccumulatorT

Accumulator instance with collected metrics and state

required
scheduler_state SchedulerState

Scheduler's final state after execution completion

required

Returns:

Type Description
Any

Compiled benchmark instance with complete statistical results

Source code in src/guidellm/benchmark/schemas/base.py
@classmethod
@abstractmethod
def compile(
    cls, accumulator: BenchmarkAccumulatorT, scheduler_state: SchedulerState
) -> Any:
    """
    Transform accumulated metrics into final benchmark results.

    :param accumulator: Accumulator instance with collected metrics and state
    :param scheduler_state: Scheduler's final state after execution completion
    :return: Compiled benchmark instance with complete statistical results
    """

BenchmarkAccumulator

Bases: StandardBaseDict, ABC, Generic[RequestT, ResponseT]

Track and accumulate benchmark metrics during scheduler execution.

Maintains incremental metric estimates as requests are processed, enabling real-time progress monitoring and efficient metric compilation. Subclasses implement specific metric calculation strategies based on request/response characteristics and scheduler state evolution.

Source code in src/guidellm/benchmark/schemas/base.py
class BenchmarkAccumulator(StandardBaseDict, ABC, Generic[RequestT, ResponseT]):
    """
    Track and accumulate benchmark metrics during scheduler execution.

    Maintains incremental metric estimates as requests are processed, enabling
    real-time progress monitoring and efficient metric compilation. Subclasses
    implement specific metric calculation strategies based on request/response
    characteristics and scheduler state evolution.
    """

    config: BenchmarkConfig = Field(
        description="Benchmark execution configuration and constraints",
    )

    @abstractmethod
    def update_estimate(
        self,
        response: ResponseT | None,
        request: RequestT,
        info: RequestInfo,
        scheduler_state: SchedulerState,
    ):
        """
        Incrementally update metrics with completed request data.

        :param response: Backend response data if request succeeded
        :param request: Request instance submitted to backend
        :param info: Request timing, status, and execution metadata
        :param scheduler_state: Current scheduler state with queue and concurrency info
        """
        ...

update_estimate(response, request, info, scheduler_state) abstractmethod

Incrementally update metrics with completed request data.

Parameters:

Name Type Description Default
response ResponseT | None

Backend response data if request succeeded

required
request RequestT

Request instance submitted to backend

required
info RequestInfo

Request timing, status, and execution metadata

required
scheduler_state SchedulerState

Current scheduler state with queue and concurrency info

required
Source code in src/guidellm/benchmark/schemas/base.py
@abstractmethod
def update_estimate(
    self,
    response: ResponseT | None,
    request: RequestT,
    info: RequestInfo,
    scheduler_state: SchedulerState,
):
    """
    Incrementally update metrics with completed request data.

    :param response: Backend response data if request succeeded
    :param request: Request instance submitted to backend
    :param info: Request timing, status, and execution metadata
    :param scheduler_state: Current scheduler state with queue and concurrency info
    """
    ...

BenchmarkArgs

Bases: ReloadableBaseModel

Common benchmark configuration arguments.

Source code in src/guidellm/benchmark/schemas/entrypoints.py
class BenchmarkArgs(ReloadableBaseModel):
    """Common benchmark configuration arguments."""

    model_config = args_model_config()

    backend: BackendArgs = Field(  # type: ignore[assignment]
        default_factory=lambda: default_kind("openai_http"),
        description=(
            "Backend configuration to define how to send requests to the model."
        ),
        examples=[
            {
                "kind": "openai_http",
                "target": "http://localhost:8000/v1",
            }
        ],
        json_schema_extra={"argument_alias": "backend"},
    )
    profile: ProfileArgs = Field(  # type: ignore[assignment]
        default_factory=lambda: default_kind("sweep"),
        description="Profile configuration to control benchmark execution.",
        examples=[{"kind": "sweep", "sweep_size": [10.0]}],
        json_schema_extra={"argument_alias": "profile"},
    )
    constraints: list[ConstraintArgs] = Field(  # type: ignore[assignment]
        description="Execution constraints to enforce during benchmark execution",
        examples=[{"kind": "max_requests", "value": 10}],
        default_factory=list,
        json_schema_extra={"argument_alias": "constraint"},
    )
    tokenizer: DataTokenizerArgs = Field(  # type: ignore[assignment]
        default_factory=lambda: default_kind("huggingface_auto"),
        description="Tokenizer configuration",
        examples=[{"kind": "huggingface_auto"}],
        json_schema_extra={"argument_alias": "tokenizer"},
    )
    data: list[DataArgs] = Field(  # type: ignore[assignment]
        description="List of dataset sources to use in the benchmarks",
        examples=[
            {"kind": "synthetic_text", "prompt_tokens": 100, "output_tokens": 100},
            {
                "kind": "huggingface",
                "source": "my/dataset",
                "load_kwargs": {"split": "test", "name": "my_dataset"},
            },
        ],
        min_length=1,
        json_schema_extra={"argument_alias": "data"},
    )
    data_column_mapper: DataPreprocessorArgs = Field(  # type: ignore[assignment]
        default_factory=lambda: default_kind("generative_column_mapper"),
        description="Specify how to map dataset columns into prompts and outputs.",
        examples=[{"kind": "generative_column_mapper"}],
        json_schema_extra={"argument_alias": "data_column_mapper"},
    )
    data_preprocessors: list[DataPreprocessorArgs] = Field(  # type: ignore[assignment]
        default_factory=lambda: default_kind_list("encode_media"),  # type: ignore[arg-type]
        description="List of dataset preprocessors to apply to the datasets.",
        examples=[{"kind": "encode_media"}],
        json_schema_extra={"argument_alias": "data_preprocessor"},
    )
    data_finalizer: DataFinalizerArgs = Field(  # type: ignore[assignment]
        default_factory=lambda: default_kind("generative"),
        description="Finalizer for preparing data samples into requests",
        examples=[{"kind": "generative"}],
        json_schema_extra={"argument_alias": "data_finalizer"},
    )
    data_loader: DataLoaderArgs = Field(  # type: ignore[assignment]
        default_factory=lambda: default_kind("pytorch"),
        description="Specify how to load the datasets into memory.",
        examples=[{"kind": "pytorch"}],
        json_schema_extra={"argument_alias": "data_loader"},
    )
    seed: RandomArgs = Field(  # type: ignore[assignment]
        default_factory=lambda: default_kind("static"),
        description="Random configuration for reproducibility (e.g., seed value)",
        examples=[{"kind": "static", "value": 42}],
        json_schema_extra={"argument_alias": "seed"},
    )
    outputs: list[BenchmarkOutputArgs] = Field(
        default_factory=lambda: default_kind_list("json", "csv"),  # type: ignore[arg-type]
        description="Benchmark output formats and paths.",
        examples=[
            {"kind": "json", "filename": "benchmarks.json"},
        ],
        json_schema_extra={"argument_alias": "output"},
    )
    metrics: MetricsArgs = Field(  # type: ignore[assignment]
        default_factory=lambda: default_kind("generative"),
        description="Configuration for metrics collection and request sampling.",
        json_schema_extra={"argument_alias": "metrics"},
    )

BenchmarkConfig

Bases: StandardBaseDict

Encapsulate execution parameters and constraints for benchmark runs.

Defines comprehensive configuration including scheduler strategy, constraint sets, transient phase handling, metric sampling preferences, and execution metadata. Coordinates profile, request, backend, and environment configurations to enable reproducible benchmark execution with precise control over metric collection.

Source code in src/guidellm/benchmark/schemas/base.py
class BenchmarkConfig(StandardBaseDict):
    """
    Encapsulate execution parameters and constraints for benchmark runs.

    Defines comprehensive configuration including scheduler strategy, constraint
    sets, transient phase handling, metric sampling preferences, and execution
    metadata. Coordinates profile, request, backend, and environment configurations
    to enable reproducible benchmark execution with precise control over metric
    collection.
    """

    id_: str = Field(
        default_factory=lambda: str(uuid.uuid4()),
        description="Unique identifier for this benchmark execution",
    )
    run_id: str = Field(
        description="Identifier grouping related benchmark runs in a series",
    )
    run_index: int = Field(
        description="Zero-based index of this run within the benchmark series",
    )
    strategy: SchedulingStrategy = Field(
        description="Scheduler strategy controlling request execution patterns",
    )
    constraints: dict[str, dict[str, Any]] = Field(
        description="Constraint definitions applied to scheduler strategy execution",
    )
    sample_size: int | None = Field(
        default=None,
        description=(
            "Maximum number of requests per status group to retain full data for. "
            "None keeps all, 0 strips all, N > 0 uses reservoir sampling."
        ),
    )
    warmup: TransientPhaseConfig = Field(
        default_factory=TransientPhaseConfig,
        description="Warmup phase configuration excluding initial transient period",
    )
    cooldown: TransientPhaseConfig = Field(
        default_factory=TransientPhaseConfig,
        description="Cooldown phase configuration excluding final transient period",
    )
    prefer_response_metrics: bool = Field(
        default=True,
        description="Prioritize response-based metrics over request-based metrics",
    )
    profile: dict[str, Any] = Field(
        description="Profile instance coordinating multi-strategy execution",
    )
    requests: dict[str, Any] = Field(
        description="Request generation configuration and dataset metadata",
    )
    backend: dict[str, Any] = Field(
        description="Backend connection parameters and service configuration",
    )
    environment: dict[str, Any] = Field(
        description="Execution environment details and system metadata",
    )

BenchmarkMetadata

Bases: StandardBaseModel

Metadata about the benchmark scenario.

Contains information such as name, description, and tags that describe the benchmark scenario. This metadata is used for reporting and organizational purposes but does not affect benchmark execution.

Source code in src/guidellm/benchmark/schemas/entrypoints.py
class BenchmarkMetadata(StandardBaseModel):
    """
    Metadata about the benchmark scenario.

    Contains information such as name, description, and tags that describe the
    benchmark scenario. This metadata is used for reporting and organizational
    purposes but does not affect benchmark execution.
    """

    # Allow arbitrary metadata fields
    model_config = args_model_config() | ConfigDict(extra="allow")

    labels: dict[str, str] = Field(
        default_factory=dict,
        description=(
            "Key-value pairs of metadata labels which will be written to the "
            "output reports."
        ),
        examples=[{"name": "benchmark", "description": "Benchmark description"}],
    )

BenchmarkOutputArgs

Bases: PydanticClassRegistryMixin['BenchmarkOutputArgs'], ABC

Base class for output creation arguments.

This class serves as a base for defining argument models used in the creation of output instances. It inherits from PydanticClassRegistryMixin to enable automatic registration of subclasses, allowing for flexible and extensible output configurations.

Attributes:

Name Type Description
schema_discriminator str

Field name for polymorphic deserialization

Source code in src/guidellm/benchmark/schemas/output.py
class BenchmarkOutputArgs(PydanticClassRegistryMixin["BenchmarkOutputArgs"], ABC):
    """Base class for output creation arguments.

    This class serves as a base for defining argument models used in the creation
    of output instances. It inherits from PydanticClassRegistryMixin to enable
    automatic registration of subclasses, allowing for flexible and extensible
    output configurations.

    :cvar schema_discriminator: Field name for polymorphic deserialization
    """

    model_config = standard_model_config()

    schema_discriminator: ClassVar[str] = "kind"

    @classmethod
    def __pydantic_schema_base_type__(cls) -> type[BenchmarkOutputArgs]:
        """
        Return base type for polymorphic validation hierarchy.

        :return: Base BenchmarkOutputArgs class for schema validation
        """
        if cls.__name__ == "BenchmarkOutputArgs":
            return cls

        return BenchmarkOutputArgs

    kind: str = Field(
        description="Type identifier for the output configuration.",
    )

__pydantic_schema_base_type__() classmethod

Return base type for polymorphic validation hierarchy.

Returns:

Type Description
type[BenchmarkOutputArgs]

Base BenchmarkOutputArgs class for schema validation

Source code in src/guidellm/benchmark/schemas/output.py
@classmethod
def __pydantic_schema_base_type__(cls) -> type[BenchmarkOutputArgs]:
    """
    Return base type for polymorphic validation hierarchy.

    :return: Base BenchmarkOutputArgs class for schema validation
    """
    if cls.__name__ == "BenchmarkOutputArgs":
        return cls

    return BenchmarkOutputArgs

BenchmarkScenario

Bases: ReloadableBaseModel, BaseSettings

Configuration arguments for generative text benchmark execution.

Defines all parameters for benchmark setup including target endpoint, data sources, backend configuration, processing pipeline, output formatting, and execution constraints. Supports loading from scenario files and merging with runtime overrides for flexible benchmark construction from multiple sources.

Example::

# Load from built-in scenario with overrides
args = BenchmarkScenario.create(
    scenario="chat",
    spec={"backend": {"kind": "openai_http", "target": "http://localhost:8000/v1"}},
)

# Create from keyword arguments only
args = BenchmarkScenario(
    spec=BenchmarkArgs(
        backend={"kind": "openai_http", "target": "http://localhost:8000/v1"},
        data=[{"kind": "synthetic_text"}],
    ),
)
Source code in src/guidellm/benchmark/schemas/entrypoints.py
class BenchmarkScenario(ReloadableBaseModel, BaseSettings):
    """
    Configuration arguments for generative text benchmark execution.

    Defines all parameters for benchmark setup including target endpoint, data
    sources, backend configuration, processing pipeline, output formatting, and
    execution constraints. Supports loading from scenario files and merging with
    runtime overrides for flexible benchmark construction from multiple sources.

    Example::

        # Load from built-in scenario with overrides
        args = BenchmarkScenario.create(
            scenario="chat",
            spec={"backend": {"kind": "openai_http", "target": "http://localhost:8000/v1"}},
        )

        # Create from keyword arguments only
        args = BenchmarkScenario(
            spec=BenchmarkArgs(
                backend={"kind": "openai_http", "target": "http://localhost:8000/v1"},
                data=[{"kind": "synthetic_text"}],
            ),
        )
    """

    model_config = SettingsConfigDict(
        env_prefix="GUIDELLM__",
        env_nested_delimiter="__",
        validate_default=True,
    )

    @classmethod
    def create(cls, scenario: Path | str | None, **kwargs: Any) -> BenchmarkScenario:
        """
        Create benchmark args from scenario file and keyword arguments.

        Loads base configuration from scenario file (built-in or custom) and merges
        with provided keyword arguments. Arguments explicitly set via kwargs override
        scenario values, while defaulted kwargs are ignored to preserve scenario
        settings.

        :param scenario: Path to scenario file, built-in scenario name, or None
        :param kwargs: Keyword arguments to override scenario values
        :return: Configured benchmark args instance
        :raises ValueError: If scenario is not found or file format is unsupported
        """
        constructor_kwargs = {}

        if scenario is not None:
            if isinstance(scenario, str) and scenario in (
                builtin_scenarios := get_builtin_scenarios()
            ):
                scenario_path = builtin_scenarios[scenario]
            elif Path(scenario).exists() and Path(scenario).is_file():
                scenario_path = Path(scenario)
            else:
                raise ValueError(f"Scenario '{scenario}' not found.")

            with scenario_path.open() as file:
                if scenario_path.suffix == ".json":
                    scenario_data = json.load(file)
                elif scenario_path.suffix in {".yaml", ".yml"}:
                    scenario_data = yaml.safe_load(file)
                else:
                    raise ValueError(
                        f"Unsupported scenario file format: {scenario_path.suffix}"
                    )
            # NOTE: If the scenario file is a report, it contains a "config" key with
            # the benchmark configuration. This is a hack and should be replaced.
            if "config" in scenario_data:
                # loading from a report file
                scenario_data = scenario_data["config"]
            constructor_kwargs.update(scenario_data)

        # NOTE In the future replace deep_update with a more intelligent merging
        #      strategy that accounts for changes to `kind`.
        # Apply overrides from kwargs
        deep_update(constructor_kwargs, kwargs)

        return cls.model_validate(constructor_kwargs)

    def get_benchmarks(self) -> list[BenchmarkArgs]:
        """
        Get list of benchmark argument instances for each individual benchmark.

        Combines global arguments with individual benchmark overrides to produce a
        list of fully configured benchmark argument instances for execution.

        :return: List of benchmark argument instances
        """
        parser = ArgStringParser(allow_overwrite=True)
        benchmarks = []
        for benchmark_override in self.benchmarks:
            if benchmark_override is None:
                benchmarks.append(self.spec.model_copy(deep=True))
            else:
                # Create a copy of the common args to apply overrides to
                benchmark_args = self.spec.model_dump(mode="python")
                for key, value in benchmark_override.items():
                    parser.set(benchmark_args, key, value)
                benchmarks.append(BenchmarkArgs.model_validate(benchmark_args))

        return benchmarks

    metadata: BenchmarkMetadata = Field(
        default_factory=BenchmarkMetadata,
        description=(
            "User metadata to describe the benchmark run. This data is written "
            "to the output file but not otherwise used by GuideLLM)."
        ),
        examples=[
            {"labels": {"name": "benchmark", "description": "Benchmark description"}}
        ],
    )
    spec: BenchmarkArgs = Field(
        default_factory=BenchmarkArgs,  # type: ignore[arg-type]
        description="Global configuration parameters for benchmark execution.",
        examples=[
            {
                "backend": {
                    "kind": "openai_http",
                    "target": "http://localhost:8000/v1",
                },
                "data": [{"kind": "synthetic_text"}],
            }
        ],
    )
    benchmarks: list[dict[str, Any] | None] = Field(
        default_factory=lambda: [None],  # type: ignore[arg-type]
        description=(
            "Individual benchmark parameter overrides. This allows overriding "
            "parameters and constraints for each benchmark run by a profile."
        ),
        min_length=1,
        examples=[
            {"profile.rate": 10.0, "constraints[0].seconds": 10},
            {"profile.rate": 20.0, "constraints[0].seconds": 20},
        ],
    )

    @model_validator(mode="before")
    @classmethod
    def insert_first_benchmark(cls, data: Any) -> Any:
        """
        Inserts the first benchmark into the common args.

        This allows users to ommit fields from the common args if they have overrides
        in the first benchmark.
        """
        if not isinstance(data, dict):
            return data

        if "benchmarks" not in data or not data["benchmarks"]:
            # No benchmarks provided, insert a blank one
            data["benchmarks"] = [None]

        first_benchmark: dict[str, Any] | None = data["benchmarks"][0]
        if isinstance(first_benchmark, dict) and first_benchmark:
            # Ensure "spec" field exists for the parser to insert into
            data["spec"] = data.get("spec", {})
            parser = ArgStringParser(allow_overwrite=True)

            # Insert the first benchmark into the common args
            # Create fields recursively.
            for key, value in first_benchmark.items():
                parser.set(data["spec"], key, value)

        return data

create(scenario, **kwargs) classmethod

Create benchmark args from scenario file and keyword arguments.

Loads base configuration from scenario file (built-in or custom) and merges with provided keyword arguments. Arguments explicitly set via kwargs override scenario values, while defaulted kwargs are ignored to preserve scenario settings.

Parameters:

Name Type Description Default
scenario Path | str | None

Path to scenario file, built-in scenario name, or None

required
kwargs Any

Keyword arguments to override scenario values

{}

Returns:

Type Description
BenchmarkScenario

Configured benchmark args instance

Raises:

Type Description
ValueError

If scenario is not found or file format is unsupported

Source code in src/guidellm/benchmark/schemas/entrypoints.py
@classmethod
def create(cls, scenario: Path | str | None, **kwargs: Any) -> BenchmarkScenario:
    """
    Create benchmark args from scenario file and keyword arguments.

    Loads base configuration from scenario file (built-in or custom) and merges
    with provided keyword arguments. Arguments explicitly set via kwargs override
    scenario values, while defaulted kwargs are ignored to preserve scenario
    settings.

    :param scenario: Path to scenario file, built-in scenario name, or None
    :param kwargs: Keyword arguments to override scenario values
    :return: Configured benchmark args instance
    :raises ValueError: If scenario is not found or file format is unsupported
    """
    constructor_kwargs = {}

    if scenario is not None:
        if isinstance(scenario, str) and scenario in (
            builtin_scenarios := get_builtin_scenarios()
        ):
            scenario_path = builtin_scenarios[scenario]
        elif Path(scenario).exists() and Path(scenario).is_file():
            scenario_path = Path(scenario)
        else:
            raise ValueError(f"Scenario '{scenario}' not found.")

        with scenario_path.open() as file:
            if scenario_path.suffix == ".json":
                scenario_data = json.load(file)
            elif scenario_path.suffix in {".yaml", ".yml"}:
                scenario_data = yaml.safe_load(file)
            else:
                raise ValueError(
                    f"Unsupported scenario file format: {scenario_path.suffix}"
                )
        # NOTE: If the scenario file is a report, it contains a "config" key with
        # the benchmark configuration. This is a hack and should be replaced.
        if "config" in scenario_data:
            # loading from a report file
            scenario_data = scenario_data["config"]
        constructor_kwargs.update(scenario_data)

    # NOTE In the future replace deep_update with a more intelligent merging
    #      strategy that accounts for changes to `kind`.
    # Apply overrides from kwargs
    deep_update(constructor_kwargs, kwargs)

    return cls.model_validate(constructor_kwargs)

get_benchmarks()

Get list of benchmark argument instances for each individual benchmark.

Combines global arguments with individual benchmark overrides to produce a list of fully configured benchmark argument instances for execution.

Returns:

Type Description
list[BenchmarkArgs]

List of benchmark argument instances

Source code in src/guidellm/benchmark/schemas/entrypoints.py
def get_benchmarks(self) -> list[BenchmarkArgs]:
    """
    Get list of benchmark argument instances for each individual benchmark.

    Combines global arguments with individual benchmark overrides to produce a
    list of fully configured benchmark argument instances for execution.

    :return: List of benchmark argument instances
    """
    parser = ArgStringParser(allow_overwrite=True)
    benchmarks = []
    for benchmark_override in self.benchmarks:
        if benchmark_override is None:
            benchmarks.append(self.spec.model_copy(deep=True))
        else:
            # Create a copy of the common args to apply overrides to
            benchmark_args = self.spec.model_dump(mode="python")
            for key, value in benchmark_override.items():
                parser.set(benchmark_args, key, value)
            benchmarks.append(BenchmarkArgs.model_validate(benchmark_args))

    return benchmarks

insert_first_benchmark(data) classmethod

Inserts the first benchmark into the common args.

This allows users to ommit fields from the common args if they have overrides in the first benchmark.

Source code in src/guidellm/benchmark/schemas/entrypoints.py
@model_validator(mode="before")
@classmethod
def insert_first_benchmark(cls, data: Any) -> Any:
    """
    Inserts the first benchmark into the common args.

    This allows users to ommit fields from the common args if they have overrides
    in the first benchmark.
    """
    if not isinstance(data, dict):
        return data

    if "benchmarks" not in data or not data["benchmarks"]:
        # No benchmarks provided, insert a blank one
        data["benchmarks"] = [None]

    first_benchmark: dict[str, Any] | None = data["benchmarks"][0]
    if isinstance(first_benchmark, dict) and first_benchmark:
        # Ensure "spec" field exists for the parser to insert into
        data["spec"] = data.get("spec", {})
        parser = ArgStringParser(allow_overwrite=True)

        # Insert the first benchmark into the common args
        # Create fields recursively.
        for key, value in first_benchmark.items():
            parser.set(data["spec"], key, value)

    return data

GenerativeAudioMetricsSummary

Bases: StandardBaseDict

Audio-specific metric summaries for generative benchmarks.

Tracks token, sample count, duration, and byte-level metrics across input, output, and total usage for audio generation workloads.

Source code in src/guidellm/benchmark/schemas/metrics.py
class GenerativeAudioMetricsSummary(StandardBaseDict):
    """
    Audio-specific metric summaries for generative benchmarks.

    Tracks token, sample count, duration, and byte-level metrics across input,
    output, and total usage for audio generation workloads.
    """

    tokens: GenerativeMetricsSummary | None = Field(
        description="Audio token count metrics and distributions"
    )
    samples: GenerativeMetricsSummary | None = Field(
        description="Sample count metrics and distributions"
    )
    seconds: GenerativeMetricsSummary | None = Field(
        description="Duration metrics in seconds and distributions"
    )
    bytes: GenerativeMetricsSummary | None = Field(
        description="Byte size metrics and distributions"
    )

    @classmethod
    def compile(
        cls,
        successful: list[GenerativeRequestStats],
        incomplete: list[GenerativeRequestStats],
        errored: list[GenerativeRequestStats],
    ) -> GenerativeAudioMetricsSummary:
        """
        Compile audio metrics summary from request statistics.

        :param successful: Successfully completed request statistics
        :param incomplete: Incomplete/cancelled request statistics
        :param errored: Failed request statistics
        :return: Compiled audio metrics summary
        """
        return GenerativeAudioMetricsSummary(
            tokens=GenerativeMetricsSummary.compile(
                property_name="audio_tokens",
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            samples=GenerativeMetricsSummary.compile(
                property_name="audio_samples",
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            seconds=GenerativeMetricsSummary.compile(
                property_name="audio_seconds",
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            bytes=GenerativeMetricsSummary.compile(
                property_name="audio_bytes",
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
        )

compile(successful, incomplete, errored) classmethod

Compile audio metrics summary from request statistics.

Parameters:

Name Type Description Default
successful list[GenerativeRequestStats]

Successfully completed request statistics

required
incomplete list[GenerativeRequestStats]

Incomplete/cancelled request statistics

required
errored list[GenerativeRequestStats]

Failed request statistics

required

Returns:

Type Description
GenerativeAudioMetricsSummary

Compiled audio metrics summary

Source code in src/guidellm/benchmark/schemas/metrics.py
@classmethod
def compile(
    cls,
    successful: list[GenerativeRequestStats],
    incomplete: list[GenerativeRequestStats],
    errored: list[GenerativeRequestStats],
) -> GenerativeAudioMetricsSummary:
    """
    Compile audio metrics summary from request statistics.

    :param successful: Successfully completed request statistics
    :param incomplete: Incomplete/cancelled request statistics
    :param errored: Failed request statistics
    :return: Compiled audio metrics summary
    """
    return GenerativeAudioMetricsSummary(
        tokens=GenerativeMetricsSummary.compile(
            property_name="audio_tokens",
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        samples=GenerativeMetricsSummary.compile(
            property_name="audio_samples",
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        seconds=GenerativeMetricsSummary.compile(
            property_name="audio_seconds",
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        bytes=GenerativeMetricsSummary.compile(
            property_name="audio_bytes",
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
    )

GenerativeBenchmark

Bases: Benchmark[GenerativeBenchmarkAccumulator]

Complete generative AI benchmark results with specialized metrics.

Encapsulates comprehensive performance data from scheduler-driven generative workload executions including request-level statistics, token/latency distributions, throughput analysis, and concurrency patterns. Provides computed fields for temporal analysis and status-grouped request details for detailed post-execution reporting.

Source code in src/guidellm/benchmark/schemas/benchmark.py
class GenerativeBenchmark(Benchmark[GenerativeBenchmarkAccumulator]):
    """Complete generative AI benchmark results with specialized metrics.

    Encapsulates comprehensive performance data from scheduler-driven generative
    workload executions including request-level statistics, token/latency distributions,
    throughput analysis, and concurrency patterns. Provides computed fields for temporal
    analysis and status-grouped request details for detailed post-execution reporting.
    """

    type_: Literal["generative_benchmark"] = "generative_benchmark"  # type: ignore[assignment]

    config: BenchmarkConfig = Field(
        description="Configuration parameters for this benchmark execution",
    )
    scheduler_state: SchedulerState = Field(
        description="Final state of the scheduler after benchmark completion",
    )
    scheduler_metrics: SchedulerMetrics = Field(
        description="Scheduler timing and performance statistics",
    )
    metrics: GenerativeMetrics = Field(
        description="Performance metrics and statistical distributions",
    )
    requests: StatusBreakdown[
        list[GenerativeRequestStats],
        list[GenerativeRequestStats],
        list[GenerativeRequestStats],
        None,
    ] = Field(
        description=(
            "Request details grouped by status: successful, incomplete, errored"
        ),
    )

    @computed_field  # type: ignore[prop-decorator]
    @property
    def start_time(self) -> float:
        """
        :return: Benchmark start time in seconds since epoch
        """
        return self.scheduler_metrics.measure_start_time

    @computed_field  # type: ignore[prop-decorator]
    @property
    def end_time(self) -> float:
        """
        :return: Benchmark end time in seconds since epoch
        """
        return self.scheduler_metrics.measure_end_time

    @computed_field  # type: ignore[prop-decorator]
    @property
    def duration(self) -> float:
        """
        :return: Total benchmark execution duration in seconds
        """
        return self.end_time - self.start_time

    @computed_field  # type: ignore[prop-decorator]
    @property
    def warmup_duration(self) -> float:
        """
        :return: Warmup phase duration in seconds
        """
        return (
            self.scheduler_metrics.measure_start_time
            - self.scheduler_metrics.request_start_time
        )

    @computed_field  # type: ignore[prop-decorator]
    @property
    def cooldown_duration(self) -> float:
        """
        :return: Cooldown phase duration in seconds
        """
        return (
            self.scheduler_metrics.request_end_time
            - self.scheduler_metrics.measure_end_time
        )

    @property
    def request_latency(self) -> StatusDistributionSummary:
        """
        :return: Statistical distribution of request latencies across all requests
        """
        return self.metrics.request_latency

    @property
    def request_throughput(self) -> StatusDistributionSummary:
        """
        :return: Statistical distribution of throughput measured in requests per second
        """
        return self.metrics.requests_per_second

    @property
    def request_concurrency(self) -> StatusDistributionSummary:
        """
        :return: Statistical distribution of concurrent requests throughout execution
        """
        return self.metrics.request_concurrency

    @classmethod
    def compile(
        cls,
        accumulator: GenerativeBenchmarkAccumulator,
        scheduler_state: SchedulerState,
    ) -> GenerativeBenchmark:
        """
        Compile final benchmark results from accumulated execution state.

        :param accumulator: Accumulated benchmark state with request statistics
        :param scheduler_state: Final scheduler state after execution completion
        :return: Compiled generative benchmark instance with complete metrics
        """
        return GenerativeBenchmark(
            config=accumulator.config,
            scheduler_state=scheduler_state,
            scheduler_metrics=SchedulerMetrics.compile(accumulator, scheduler_state),
            metrics=GenerativeMetrics.compile(accumulator),
            requests=StatusBreakdown(
                successful=accumulator.completed.get_sampled(),
                incomplete=accumulator.incomplete.get_sampled(),
                errored=accumulator.errored.get_sampled(),
                total=None,
            ),
        )

cooldown_duration property

Returns:

Type Description
float

Cooldown phase duration in seconds

duration property

Returns:

Type Description
float

Total benchmark execution duration in seconds

end_time property

Returns:

Type Description
float

Benchmark end time in seconds since epoch

request_concurrency property

Returns:

Type Description
StatusDistributionSummary

Statistical distribution of concurrent requests throughout execution

request_latency property

Returns:

Type Description
StatusDistributionSummary

Statistical distribution of request latencies across all requests

request_throughput property

Returns:

Type Description
StatusDistributionSummary

Statistical distribution of throughput measured in requests per second

start_time property

Returns:

Type Description
float

Benchmark start time in seconds since epoch

warmup_duration property

Returns:

Type Description
float

Warmup phase duration in seconds

compile(accumulator, scheduler_state) classmethod

Compile final benchmark results from accumulated execution state.

Parameters:

Name Type Description Default
accumulator GenerativeBenchmarkAccumulator

Accumulated benchmark state with request statistics

required
scheduler_state SchedulerState

Final scheduler state after execution completion

required

Returns:

Type Description
GenerativeBenchmark

Compiled generative benchmark instance with complete metrics

Source code in src/guidellm/benchmark/schemas/benchmark.py
@classmethod
def compile(
    cls,
    accumulator: GenerativeBenchmarkAccumulator,
    scheduler_state: SchedulerState,
) -> GenerativeBenchmark:
    """
    Compile final benchmark results from accumulated execution state.

    :param accumulator: Accumulated benchmark state with request statistics
    :param scheduler_state: Final scheduler state after execution completion
    :return: Compiled generative benchmark instance with complete metrics
    """
    return GenerativeBenchmark(
        config=accumulator.config,
        scheduler_state=scheduler_state,
        scheduler_metrics=SchedulerMetrics.compile(accumulator, scheduler_state),
        metrics=GenerativeMetrics.compile(accumulator),
        requests=StatusBreakdown(
            successful=accumulator.completed.get_sampled(),
            incomplete=accumulator.incomplete.get_sampled(),
            errored=accumulator.errored.get_sampled(),
            total=None,
        ),
    )

GenerativeBenchmarkAccumulator

Bases: BenchmarkAccumulator[GenerationRequest, GenerationResponse]

Primary accumulator for generative benchmark execution metrics and statistics.

Orchestrates real-time metric collection across timing, scheduler, concurrency, and generative performance dimensions. Maintains separate accumulators for completed, errored, and incomplete requests while tracking overall metrics. Integrates with scheduler state to monitor warmup/cooldown phases and compute time-weighted statistics for throughput and latency analysis.

Source code in src/guidellm/benchmark/schemas/accumulator.py
class GenerativeBenchmarkAccumulator(
    BenchmarkAccumulator[GenerationRequest, GenerationResponse]
):
    """
    Primary accumulator for generative benchmark execution metrics and statistics.

    Orchestrates real-time metric collection across timing, scheduler, concurrency, and
    generative performance dimensions. Maintains separate accumulators for completed,
    errored, and incomplete requests while tracking overall metrics. Integrates with
    scheduler state to monitor warmup/cooldown phases and compute time-weighted
    statistics for throughput and latency analysis.
    """

    timings: GenerativeBenchmarkTimings = Field(
        default_factory=GenerativeBenchmarkTimings,
        description="Timing phases and transitions during benchmark execution",
    )
    completed: GenerativeRequestsAccumulator = Field(
        default_factory=GenerativeRequestsAccumulator,
        description="Accumulator for completed requests",
    )
    errored: GenerativeRequestsAccumulator = Field(
        default_factory=GenerativeRequestsAccumulator,
        description="Accumulator for errored requests",
    )
    incomplete: GenerativeRequestsAccumulator = Field(
        default_factory=GenerativeRequestsAccumulator,
        description="Accumulator for incomplete requests",
    )
    scheduler_metrics: SchedulerMetricsAccumulator = Field(
        default_factory=SchedulerMetricsAccumulator,
        description="Running metrics for scheduler state",
    )
    concurrency_metric: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Accumulated request concurrency statistics",
    )
    total_metrics: GenerativeMetricsAccumulator = Field(
        default_factory=GenerativeMetricsAccumulator,
        description="Running metrics for all requests",
    )
    completed_metrics: GenerativeMetricsAccumulator = Field(
        default_factory=GenerativeMetricsAccumulator,
        description="Running metrics for completed requests",
    )
    errored_metrics: GenerativeMetricsAccumulator = Field(
        default_factory=GenerativeMetricsAccumulator,
        description="Running metrics for errored requests",
    )
    incomplete_metrics: GenerativeMetricsAccumulator = Field(
        default_factory=GenerativeMetricsAccumulator,
        description="Running metrics for incomplete requests",
    )

    def model_post_init(self, __context):
        """
        Initialize child accumulators with config values after model construction.

        Propagates sample_size from config to child request accumulators to ensure
        consistent sampling behavior across completed, errored, and incomplete request
        collections. This ensures the --metrics-sample-size option functions correctly.
        """
        super().model_post_init(__context)

        # Propagate sample_size from config to child accumulators
        self.completed.sample_size = self.config.sample_size
        self.errored.sample_size = self.config.sample_size
        self.incomplete.sample_size = self.config.sample_size

    def update_estimate(
        self,
        response: GenerationResponse | None,
        request: GenerationRequest,
        info: RequestInfo,
        scheduler_state: SchedulerState,
    ):
        """
        Update all benchmark metrics with a completed request.

        Processes request completion by updating timing phases, concurrency metrics,
        scheduler statistics, and generative performance metrics. Routes request to
        appropriate status-specific accumulator (completed/errored/incomplete) and
        updates aggregate totals. Cancelled requests that never started are ignored.

        :param response: Generation response with output and metrics, or None
        :param request: Original generation request with input data
        :param info: Request execution information and timing
        :param scheduler_state: Current scheduler state for phase tracking
        """
        self.timings.update_estimate(info, scheduler_state, self.config)
        duration = self.timings.duration
        elapsed_time_last_update = self.timings.elapsed_time_last_update
        self.concurrency_metric.update_estimate(
            value=scheduler_state.processing_requests,
            duration=duration,
            elapsed=elapsed_time_last_update,
        )

        requests_accumulator: GenerativeRequestsAccumulator
        metrics_accumulator: GenerativeMetricsAccumulator

        if info.status == "completed":
            requests_accumulator = self.completed
            metrics_accumulator = self.completed_metrics
        elif info.status == "errored":
            requests_accumulator = self.errored
            metrics_accumulator = self.errored_metrics
        elif info.status == "cancelled" and info.timings.resolve_start is not None:
            requests_accumulator = self.incomplete
            metrics_accumulator = self.incomplete_metrics
        else:
            # Not a terminal status or cancelled before starting
            # Do not include in requests or metrics
            return

        stats = requests_accumulator.update_estimate(
            response, request, info, self.config.prefer_response_metrics
        )
        metrics_accumulator.update_estimate(stats, duration)
        self.total_metrics.update_estimate(stats, duration)
        self.scheduler_metrics.update_estimate(scheduler_state, stats)

model_post_init(__context)

Initialize child accumulators with config values after model construction.

Propagates sample_size from config to child request accumulators to ensure consistent sampling behavior across completed, errored, and incomplete request collections. This ensures the --metrics-sample-size option functions correctly.

Source code in src/guidellm/benchmark/schemas/accumulator.py
def model_post_init(self, __context):
    """
    Initialize child accumulators with config values after model construction.

    Propagates sample_size from config to child request accumulators to ensure
    consistent sampling behavior across completed, errored, and incomplete request
    collections. This ensures the --metrics-sample-size option functions correctly.
    """
    super().model_post_init(__context)

    # Propagate sample_size from config to child accumulators
    self.completed.sample_size = self.config.sample_size
    self.errored.sample_size = self.config.sample_size
    self.incomplete.sample_size = self.config.sample_size

update_estimate(response, request, info, scheduler_state)

Update all benchmark metrics with a completed request.

Processes request completion by updating timing phases, concurrency metrics, scheduler statistics, and generative performance metrics. Routes request to appropriate status-specific accumulator (completed/errored/incomplete) and updates aggregate totals. Cancelled requests that never started are ignored.

Parameters:

Name Type Description Default
response GenerationResponse | None

Generation response with output and metrics, or None

required
request GenerationRequest

Original generation request with input data

required
info RequestInfo

Request execution information and timing

required
scheduler_state SchedulerState

Current scheduler state for phase tracking

required
Source code in src/guidellm/benchmark/schemas/accumulator.py
def update_estimate(
    self,
    response: GenerationResponse | None,
    request: GenerationRequest,
    info: RequestInfo,
    scheduler_state: SchedulerState,
):
    """
    Update all benchmark metrics with a completed request.

    Processes request completion by updating timing phases, concurrency metrics,
    scheduler statistics, and generative performance metrics. Routes request to
    appropriate status-specific accumulator (completed/errored/incomplete) and
    updates aggregate totals. Cancelled requests that never started are ignored.

    :param response: Generation response with output and metrics, or None
    :param request: Original generation request with input data
    :param info: Request execution information and timing
    :param scheduler_state: Current scheduler state for phase tracking
    """
    self.timings.update_estimate(info, scheduler_state, self.config)
    duration = self.timings.duration
    elapsed_time_last_update = self.timings.elapsed_time_last_update
    self.concurrency_metric.update_estimate(
        value=scheduler_state.processing_requests,
        duration=duration,
        elapsed=elapsed_time_last_update,
    )

    requests_accumulator: GenerativeRequestsAccumulator
    metrics_accumulator: GenerativeMetricsAccumulator

    if info.status == "completed":
        requests_accumulator = self.completed
        metrics_accumulator = self.completed_metrics
    elif info.status == "errored":
        requests_accumulator = self.errored
        metrics_accumulator = self.errored_metrics
    elif info.status == "cancelled" and info.timings.resolve_start is not None:
        requests_accumulator = self.incomplete
        metrics_accumulator = self.incomplete_metrics
    else:
        # Not a terminal status or cancelled before starting
        # Do not include in requests or metrics
        return

    stats = requests_accumulator.update_estimate(
        response, request, info, self.config.prefer_response_metrics
    )
    metrics_accumulator.update_estimate(stats, duration)
    self.total_metrics.update_estimate(stats, duration)
    self.scheduler_metrics.update_estimate(scheduler_state, stats)

GenerativeBenchmarkMetadata

Bases: StandardBaseModel

Versioning and environment metadata for generative benchmark reports.

Source code in src/guidellm/benchmark/schemas/report.py
class GenerativeBenchmarkMetadata(StandardBaseModel):
    """
    Versioning and environment metadata for generative benchmark reports.
    """

    # Make sure to update version when making breaking changes to report schema
    version: Literal[2] = Field(
        description=(
            "Version of the benchmark report schema, increments "
            "whenever there is a breaking change to the output format"
        ),
        default=2,
    )
    guidellm_version: str = Field(
        description="Version of the guidellm package used for the benchmark",
        default_factory=lambda: version("guidellm"),
    )
    python_version: str = Field(
        description="Version of Python interpreter used during the benchmark",
        default_factory=platform.python_version,
    )
    platform: str = Field(
        description="Operating system platform where the benchmark was executed",
        default_factory=platform.platform,
    )

GenerativeBenchmarkTimings

Bases: StandardBaseModel

Tracks timing phases and transitions during benchmark execution.

Monitors timestamps throughout benchmark execution including request submission, measurement period boundaries (warmup/active/cooldown), and completion events. Provides duration calculations and phase status determination based on configured warmup and cooldown periods.

Source code in src/guidellm/benchmark/schemas/accumulator.py
class GenerativeBenchmarkTimings(StandardBaseModel):
    """
    Tracks timing phases and transitions during benchmark execution.

    Monitors timestamps throughout benchmark execution including request submission,
    measurement period boundaries (warmup/active/cooldown), and completion events.
    Provides duration calculations and phase status determination based on configured
    warmup and cooldown periods.
    """

    request_start: float | None = Field(
        description="Timestamp when the first request was sent", default=None
    )
    measure_start: float | None = Field(
        description="Timestamp when measurement period started", default=None
    )
    measure_end: float | None = Field(
        description="Timestamp when measurement period ended", default=None
    )
    request_end: float | None = Field(
        description="Timestamp when the last request was completed", default=None
    )
    current_update: float | None = Field(
        description="Most recent timestamp observed during execution", default=None
    )
    current_request: float | None = Field(
        description="Most recent request completion timestamp observed", default=None
    )
    last_update: float | None = Field(
        description="Previous timestamp observed before the current one", default=None
    )
    last_request: float | None = Field(
        description="Previous request completion timestamp before the current one",
        default=None,
    )

    @property
    def status(self) -> Literal["pending", "warmup", "active", "cooldown"]:
        """
        :return: Current execution phase based on timing thresholds
        """
        if self.request_start is None or self.current_update is None:
            return "pending"

        if self.measure_start is None or self.current_update <= self.measure_start:
            return "warmup"

        if self.measure_end is not None and self.current_update >= self.measure_end:
            return "cooldown"

        return "active"

    @property
    def duration(self) -> float:
        """
        :return: Elapsed time since measurement or request start in seconds
        """
        if self.request_start is None or self.current_update is None:
            return 0.0

        return self.current_update - self.request_start

    @property
    def elapsed_time_last_update(self) -> float:
        """
        :return: Time elapsed between the last two update timestamps in seconds
        """
        if self.current_update is None or self.last_update is None:
            return 0.0

        return self.current_update - self.last_update

    @property
    def elapsed_time_last_request(self) -> float:
        """
        :return: Time elapsed between the last two request completions in seconds
        """
        if self.current_request is None or self.last_request is None:
            return 0.0

        return self.current_request - self.last_request

    @property
    def finalized_request_start(self) -> float:
        """
        :return: Finalized timestamp from the current state for when requests started
        """
        return self.request_start or -1.0

    @property
    def finalized_measure_start(self) -> float:
        """
        :return: Finalized timestamp from the current state for when measurement started
        """
        return self.measure_start or self.finalized_request_start

    @property
    def finalized_measure_end(self) -> float:
        """
        :return: Finalized timestamp from the current state for when measurement ended
        """
        return self.measure_end or self.finalized_request_end

    @property
    def finalized_request_end(self) -> float:
        """
        :return: Finalized timestamp from the current state for when requests ended
        """
        return self.request_end or self.current_request or -1.0

    def update_estimate(
        self,
        info: RequestInfo,
        scheduler_state: SchedulerState,
        config: BenchmarkConfig,
    ):
        """
        Update timing estimates based on request info and scheduler state.

        Advances timing markers through benchmark phases (warmup to active to cooldown)
        based on configured thresholds. Updates current/last timestamps for updates and
        request completions, determining measurement period boundaries.

        :param info: Request information containing timing data
        :param scheduler_state: Current scheduler state with progress metrics
        :param config: Benchmark configuration with warmup/cooldown settings
        """
        # First update non terminal timestamps
        self.request_start = scheduler_state.start_requests_time
        self.last_update = self.current_update
        if (current_time := info.timings.last_reported) is not None:
            self.current_update = (
                current_time
                if self.current_update is None
                else max(self.current_update, current_time)
            )

        # Next update measurement period timestamps, if available and possible
        warmup_active, measure_start = config.warmup.compute_transition_time(
            info=info, state=scheduler_state, period="start"
        )
        if not warmup_active:
            # No warmup, set measure_start to first request start
            self.measure_start = self.request_start
        elif measure_start is not None:
            self.measure_start = measure_start
        cooldown_active, measure_end = config.cooldown.compute_transition_time(
            info=info, state=scheduler_state, period="end"
        )
        if cooldown_active and measure_end is not None:
            self.measure_end = measure_end

        # Update last request terminal timestamps, if request is terminal
        if info.status in {"completed", "errored", "cancelled"}:
            self.last_request = self.current_request
            if info.completed_at is not None and (
                self.current_request is None or info.completed_at > self.current_request
            ):
                self.current_request = info.completed_at

        # Finally, update request stop timestamps, if at that stage and available
        if scheduler_state.end_processing_time is not None and self.request_end is None:
            self.request_end = (
                scheduler_state.progress.stop_time
                or self.current_request
                or scheduler_state.end_processing_time
            )
            if self.measure_end is None:
                # No cooldown triggered, set measure_end to request_end
                self.measure_end = self.request_end

duration property

Returns:

Type Description
float

Elapsed time since measurement or request start in seconds

elapsed_time_last_request property

Returns:

Type Description
float

Time elapsed between the last two request completions in seconds

elapsed_time_last_update property

Returns:

Type Description
float

Time elapsed between the last two update timestamps in seconds

finalized_measure_end property

Returns:

Type Description
float

Finalized timestamp from the current state for when measurement ended

finalized_measure_start property

Returns:

Type Description
float

Finalized timestamp from the current state for when measurement started

finalized_request_end property

Returns:

Type Description
float

Finalized timestamp from the current state for when requests ended

finalized_request_start property

Returns:

Type Description
float

Finalized timestamp from the current state for when requests started

status property

Returns:

Type Description
Literal['pending', 'warmup', 'active', 'cooldown']

Current execution phase based on timing thresholds

update_estimate(info, scheduler_state, config)

Update timing estimates based on request info and scheduler state.

Advances timing markers through benchmark phases (warmup to active to cooldown) based on configured thresholds. Updates current/last timestamps for updates and request completions, determining measurement period boundaries.

Parameters:

Name Type Description Default
info RequestInfo

Request information containing timing data

required
scheduler_state SchedulerState

Current scheduler state with progress metrics

required
config BenchmarkConfig

Benchmark configuration with warmup/cooldown settings

required
Source code in src/guidellm/benchmark/schemas/accumulator.py
def update_estimate(
    self,
    info: RequestInfo,
    scheduler_state: SchedulerState,
    config: BenchmarkConfig,
):
    """
    Update timing estimates based on request info and scheduler state.

    Advances timing markers through benchmark phases (warmup to active to cooldown)
    based on configured thresholds. Updates current/last timestamps for updates and
    request completions, determining measurement period boundaries.

    :param info: Request information containing timing data
    :param scheduler_state: Current scheduler state with progress metrics
    :param config: Benchmark configuration with warmup/cooldown settings
    """
    # First update non terminal timestamps
    self.request_start = scheduler_state.start_requests_time
    self.last_update = self.current_update
    if (current_time := info.timings.last_reported) is not None:
        self.current_update = (
            current_time
            if self.current_update is None
            else max(self.current_update, current_time)
        )

    # Next update measurement period timestamps, if available and possible
    warmup_active, measure_start = config.warmup.compute_transition_time(
        info=info, state=scheduler_state, period="start"
    )
    if not warmup_active:
        # No warmup, set measure_start to first request start
        self.measure_start = self.request_start
    elif measure_start is not None:
        self.measure_start = measure_start
    cooldown_active, measure_end = config.cooldown.compute_transition_time(
        info=info, state=scheduler_state, period="end"
    )
    if cooldown_active and measure_end is not None:
        self.measure_end = measure_end

    # Update last request terminal timestamps, if request is terminal
    if info.status in {"completed", "errored", "cancelled"}:
        self.last_request = self.current_request
        if info.completed_at is not None and (
            self.current_request is None or info.completed_at > self.current_request
        ):
            self.current_request = info.completed_at

    # Finally, update request stop timestamps, if at that stage and available
    if scheduler_state.end_processing_time is not None and self.request_end is None:
        self.request_end = (
            scheduler_state.progress.stop_time
            or self.current_request
            or scheduler_state.end_processing_time
        )
        if self.measure_end is None:
            # No cooldown triggered, set measure_end to request_end
            self.measure_end = self.request_end

GenerativeBenchmarksReport

Bases: StandardBaseModel

Container for multiple benchmark results with load/save functionality.

Aggregates multiple generative benchmark executions into a single report, providing persistence through JSON and YAML file formats. Enables result collection, storage, and retrieval across different execution sessions with automatic file type detection and path resolution.

Attributes:

Name Type Description
DEFAULT_FILE str

Default filename used when saving to or loading from a directory

Source code in src/guidellm/benchmark/schemas/report.py
class GenerativeBenchmarksReport(StandardBaseModel):
    """Container for multiple benchmark results with load/save functionality.

    Aggregates multiple generative benchmark executions into a single report,
    providing persistence through JSON and YAML file formats. Enables result
    collection, storage, and retrieval across different execution sessions with
    automatic file type detection and path resolution.

    :cvar DEFAULT_FILE: Default filename used when saving to or loading from a directory
    """

    DEFAULT_FILE: ClassVar[str] = "benchmarks.json"

    metadata: GenerativeBenchmarkMetadata = Field(
        description="Metadata about the benchmark report and execution environment",
        default_factory=GenerativeBenchmarkMetadata,
    )
    config: BenchmarkScenario = Field(
        description="Benchmark arguments used for all benchmarks in the report"
    )
    benchmarks: list[GenerativeBenchmark] = Field(
        description="List of completed benchmarks in the report",
        default_factory=list,
    )

    def save_file(
        self,
        path: str | Path | None = None,
        type_: Literal["json", "yaml"] | None = None,
    ) -> Path:
        """
        Save report to file in JSON or YAML format.

        :param path: File path or directory for saving, defaults to current directory
            with DEFAULT_FILE name
        :param type_: File format override ('json' or 'yaml'), auto-detected from
            extension if None
        :return: Resolved path to the saved file
        :raises ValueError: If file type is unsupported or cannot be determined
        """
        file_path = GenerativeBenchmarksReport._resolve_path(
            path if path is not None else Path.cwd()
        )
        file_path.parent.mkdir(parents=True, exist_ok=True)
        file_type = type_ or file_path.suffix.lower()[1:]
        model_dict = self.model_dump(mode="json")
        if file_type == "json":
            save_str = json.dumps(model_dict)
        elif file_type in ["yaml", "yml"]:
            save_str = yaml.dump(model_dict)
        else:
            raise ValueError(f"Unsupported file type: {file_type} for {file_path}.")

        with file_path.open("w") as file:
            file.write(save_str)

        return file_path

    @classmethod
    def load_file(
        cls, path: str | Path, type_: Literal["json", "yaml"] | None = None
    ) -> GenerativeBenchmarksReport:
        """
        Load report from JSON or YAML file.

        :param path: File path or directory containing DEFAULT_FILE to load from
        :param type_: File format override ('json' or 'yaml'), auto-detected from
            extension if None
        :return: Loaded report instance with benchmarks and configuration
        :raises ValueError: If file type is unsupported or cannot be determined
        :raises FileNotFoundError: If specified file does not exist
        """
        file_path = GenerativeBenchmarksReport._resolve_path(path)
        file_type = type_ or file_path.suffix.lower()[1:]

        with file_path.open("r") as file:
            if file_type == "json":
                model_dict = json.loads(file.read())
            elif file_type in ["yaml", "yml"]:
                model_dict = yaml.safe_load(file)
            else:
                raise ValueError(f"Unsupported file type: {file_type} for {file_path}.")

        return GenerativeBenchmarksReport.model_validate(model_dict)

    @classmethod
    def _resolve_path(cls, path: str | Path) -> Path:
        """
        Resolve input to file path, converting directories to DEFAULT_FILE location.

        :param path: String or Path to resolve, directories append DEFAULT_FILE
        :return: Resolved file path
        """
        resolved = Path(path) if not isinstance(path, Path) else path

        if resolved.is_dir():
            resolved = resolved / GenerativeBenchmarksReport.DEFAULT_FILE

        return resolved

load_file(path, type_=None) classmethod

Load report from JSON or YAML file.

Parameters:

Name Type Description Default
path str | Path

File path or directory containing DEFAULT_FILE to load from

required
type_ Literal['json', 'yaml'] | None

File format override ('json' or 'yaml'), auto-detected from extension if None

None

Returns:

Type Description
GenerativeBenchmarksReport

Loaded report instance with benchmarks and configuration

Raises:

Type Description
ValueError

If file type is unsupported or cannot be determined

FileNotFoundError

If specified file does not exist

Source code in src/guidellm/benchmark/schemas/report.py
@classmethod
def load_file(
    cls, path: str | Path, type_: Literal["json", "yaml"] | None = None
) -> GenerativeBenchmarksReport:
    """
    Load report from JSON or YAML file.

    :param path: File path or directory containing DEFAULT_FILE to load from
    :param type_: File format override ('json' or 'yaml'), auto-detected from
        extension if None
    :return: Loaded report instance with benchmarks and configuration
    :raises ValueError: If file type is unsupported or cannot be determined
    :raises FileNotFoundError: If specified file does not exist
    """
    file_path = GenerativeBenchmarksReport._resolve_path(path)
    file_type = type_ or file_path.suffix.lower()[1:]

    with file_path.open("r") as file:
        if file_type == "json":
            model_dict = json.loads(file.read())
        elif file_type in ["yaml", "yml"]:
            model_dict = yaml.safe_load(file)
        else:
            raise ValueError(f"Unsupported file type: {file_type} for {file_path}.")

    return GenerativeBenchmarksReport.model_validate(model_dict)

save_file(path=None, type_=None)

Save report to file in JSON or YAML format.

Parameters:

Name Type Description Default
path str | Path | None

File path or directory for saving, defaults to current directory with DEFAULT_FILE name

None
type_ Literal['json', 'yaml'] | None

File format override ('json' or 'yaml'), auto-detected from extension if None

None

Returns:

Type Description
Path

Resolved path to the saved file

Raises:

Type Description
ValueError

If file type is unsupported or cannot be determined

Source code in src/guidellm/benchmark/schemas/report.py
def save_file(
    self,
    path: str | Path | None = None,
    type_: Literal["json", "yaml"] | None = None,
) -> Path:
    """
    Save report to file in JSON or YAML format.

    :param path: File path or directory for saving, defaults to current directory
        with DEFAULT_FILE name
    :param type_: File format override ('json' or 'yaml'), auto-detected from
        extension if None
    :return: Resolved path to the saved file
    :raises ValueError: If file type is unsupported or cannot be determined
    """
    file_path = GenerativeBenchmarksReport._resolve_path(
        path if path is not None else Path.cwd()
    )
    file_path.parent.mkdir(parents=True, exist_ok=True)
    file_type = type_ or file_path.suffix.lower()[1:]
    model_dict = self.model_dump(mode="json")
    if file_type == "json":
        save_str = json.dumps(model_dict)
    elif file_type in ["yaml", "yml"]:
        save_str = yaml.dump(model_dict)
    else:
        raise ValueError(f"Unsupported file type: {file_type} for {file_path}.")

    with file_path.open("w") as file:
        file.write(save_str)

    return file_path

GenerativeImageMetricsSummary

Bases: StandardBaseDict

Image-specific metric summaries for generative benchmarks.

Tracks token, image count, pixel, and byte-level metrics across input, output, and total usage for image generation workloads.

Source code in src/guidellm/benchmark/schemas/metrics.py
class GenerativeImageMetricsSummary(StandardBaseDict):
    """
    Image-specific metric summaries for generative benchmarks.

    Tracks token, image count, pixel, and byte-level metrics across input, output,
    and total usage for image generation workloads.
    """

    tokens: GenerativeMetricsSummary | None = Field(
        description="Image token count metrics and distributions"
    )
    images: GenerativeMetricsSummary | None = Field(
        description="Image count metrics and distributions"
    )
    pixels: GenerativeMetricsSummary | None = Field(
        description="Pixel count metrics and distributions"
    )
    bytes: GenerativeMetricsSummary | None = Field(
        description="Byte size metrics and distributions"
    )

    @classmethod
    def compile(
        cls,
        successful: list[GenerativeRequestStats],
        incomplete: list[GenerativeRequestStats],
        errored: list[GenerativeRequestStats],
    ) -> GenerativeImageMetricsSummary:
        """
        Compile image metrics summary from request statistics.

        :param successful: Successfully completed request statistics
        :param incomplete: Incomplete/cancelled request statistics
        :param errored: Failed request statistics
        :return: Compiled image metrics summary
        """
        return GenerativeImageMetricsSummary(
            tokens=GenerativeMetricsSummary.compile(
                property_name="image_tokens",
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            images=GenerativeMetricsSummary.compile(
                property_name="image_count",
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            pixels=GenerativeMetricsSummary.compile(
                property_name="image_pixels",
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            bytes=GenerativeMetricsSummary.compile(
                property_name="image_bytes",
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
        )

compile(successful, incomplete, errored) classmethod

Compile image metrics summary from request statistics.

Parameters:

Name Type Description Default
successful list[GenerativeRequestStats]

Successfully completed request statistics

required
incomplete list[GenerativeRequestStats]

Incomplete/cancelled request statistics

required
errored list[GenerativeRequestStats]

Failed request statistics

required

Returns:

Type Description
GenerativeImageMetricsSummary

Compiled image metrics summary

Source code in src/guidellm/benchmark/schemas/metrics.py
@classmethod
def compile(
    cls,
    successful: list[GenerativeRequestStats],
    incomplete: list[GenerativeRequestStats],
    errored: list[GenerativeRequestStats],
) -> GenerativeImageMetricsSummary:
    """
    Compile image metrics summary from request statistics.

    :param successful: Successfully completed request statistics
    :param incomplete: Incomplete/cancelled request statistics
    :param errored: Failed request statistics
    :return: Compiled image metrics summary
    """
    return GenerativeImageMetricsSummary(
        tokens=GenerativeMetricsSummary.compile(
            property_name="image_tokens",
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        images=GenerativeMetricsSummary.compile(
            property_name="image_count",
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        pixels=GenerativeMetricsSummary.compile(
            property_name="image_pixels",
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        bytes=GenerativeMetricsSummary.compile(
            property_name="image_bytes",
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
    )

GenerativeMetrics

Bases: StandardBaseDict

Comprehensive metrics for generative AI benchmarks.

Aggregates request statistics, token metrics, timing distributions, and domain-specific measurements across text, image, video, and audio modalities. Provides detailed statistical summaries including distribution analysis for throughput, latency, concurrency, and resource utilization metrics across successful, incomplete, and errored requests.

Source code in src/guidellm/benchmark/schemas/metrics.py
class GenerativeMetrics(StandardBaseDict):
    """
    Comprehensive metrics for generative AI benchmarks.

    Aggregates request statistics, token metrics, timing distributions, and
    domain-specific measurements across text, image, video, and audio modalities.
    Provides detailed statistical summaries including distribution analysis for
    throughput, latency, concurrency, and resource utilization metrics across
    successful, incomplete, and errored requests.
    """

    # Request stats
    request_totals: StatusBreakdown[int, int, int, int] = Field(
        description="Request counts by status: successful, incomplete, errored, total"
    )
    requests_per_second: StatusDistributionSummary = Field(
        description="Distribution of requests per second across benchmark execution"
    )
    request_concurrency: StatusDistributionSummary = Field(
        description="Distribution of concurrent request counts during execution"
    )
    request_latency: StatusDistributionSummary = Field(
        description="Distribution of request latencies for completed requests"
    )
    request_streaming_iterations_count: StatusDistributionSummary = Field(
        description="Distribution of stream iterations for completed requests"
    )

    # General token stats
    prompt_token_count: StatusDistributionSummary = Field(
        description="Distribution of prompt token counts by request status"
    )
    output_token_count: StatusDistributionSummary = Field(
        description="Distribution of output token counts by request status"
    )
    total_token_count: StatusDistributionSummary = Field(
        description="Distribution of total token counts by request status"
    )
    time_to_first_token_ms: StatusDistributionSummary = Field(
        description="Distribution of first token latencies in milliseconds"
    )
    time_to_first_output_token_ms: StatusDistributionSummary = Field(
        description=(
            "Distribution of first content (non-reasoning) token latencies "
            "in milliseconds"
        )
    )
    time_per_output_token_ms: StatusDistributionSummary = Field(
        description="Distribution of average time per output token in milliseconds"
    )
    inter_token_latency_ms: StatusDistributionSummary = Field(
        description="Distribution of inter-token latencies in milliseconds"
    )
    prompt_tokens_per_second: StatusDistributionSummary = Field(
        description="Distribution of prompt token processing rates"
    )
    output_tokens_per_second: StatusDistributionSummary = Field(
        description="Distribution of output token generation rates"
    )
    tokens_per_second: StatusDistributionSummary = Field(
        description="Distribution of total token throughput including prompt and output"
    )
    output_tokens_per_iteration: StatusDistributionSummary = Field(
        description="Distribution of output tokens generated per streaming iteration"
    )
    iter_tokens_per_iteration: StatusDistributionSummary = Field(
        description=(
            "Distribution of output tokens (without first) generated per "
            "streaming iteration"
        )
    )

    # Domain specific stats
    text: GenerativeTextMetricsSummary = Field(
        description="Text-specific metrics for tokens, words, and characters"
    )
    image: GenerativeImageMetricsSummary = Field(
        description="Image-specific metrics for tokens, images, pixels, and bytes"
    )
    video: GenerativeVideoMetricsSummary = Field(
        description="Video-specific metrics for tokens, frames, duration, and bytes"
    )
    audio: GenerativeAudioMetricsSummary = Field(
        description="Audio-specific metrics for tokens, samples, duration, and bytes"
    )
    tool_call: GenerativeToolCallMetricsSummary = Field(
        description="Tool call metrics for tokens and call counts"
    )

    @classmethod
    def compile(cls, accumulator: GenerativeBenchmarkAccumulator) -> GenerativeMetrics:
        """
        Compile comprehensive generative metrics from benchmark accumulator.

        :param accumulator: Benchmark accumulator with completed request statistics
        :return: Compiled generative metrics with all distributions and summaries
        :raises ValueError: If measure_start and measure_end/request_end are not set
        """
        start_time = accumulator.timings.finalized_measure_start
        end_time = accumulator.timings.finalized_measure_end

        if start_time == -1.0 or end_time == -1.0:
            raise ValueError(
                "Cannot compile GenerativeMetrics: "
                "No measurement start or end times available."
            )

        successful = accumulator.completed.get_within_range(start_time, end_time)
        incomplete = accumulator.incomplete.get_within_range(start_time, end_time)
        errored = accumulator.errored.get_within_range(start_time, end_time)

        return GenerativeMetrics(
            # Request stats
            request_totals=StatusBreakdown(
                successful=len(successful),
                incomplete=len(incomplete),
                errored=len(errored),
                total=(len(successful) + len(incomplete) + len(errored)),
            ),
            requests_per_second=StatusDistributionSummary.rate_distribution_from_timings_function(
                function=lambda req: req.request_end_time,
                successful=successful,
                incomplete=incomplete,
                errored=errored,
                start_time=start_time,
                end_time=end_time,
            ),
            request_concurrency=StatusDistributionSummary.concurrency_distribution_from_timings_function(
                function=(
                    lambda req: (
                        (req.request_start_time, req.request_end_time)
                        if req.request_start_time is not None
                        and req.request_end_time is not None
                        else None
                    )
                ),
                successful=successful,
                incomplete=incomplete,
                errored=errored,
                start_time=start_time,
                end_time=end_time,
            ),
            request_latency=StatusDistributionSummary.from_values_function(
                function=lambda req: req.request_latency or 0.0,
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            request_streaming_iterations_count=StatusDistributionSummary.from_values_function(
                function=lambda req: req.info.timings.request_iterations or 0.0,
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            # General token stats
            prompt_token_count=StatusDistributionSummary.from_values_function(
                function=lambda req: req.prompt_tokens or 0.0,
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            output_token_count=StatusDistributionSummary.from_values_function(
                function=lambda req: req.output_tokens or 0.0,
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            total_token_count=StatusDistributionSummary.from_values_function(
                function=lambda req: req.total_tokens or 0.0,
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            # TODO: Need to evaluate closed=False vs closed=True for first-token
            # latencies. See github.com/vllm-project/guidellm/issues/1078
            time_to_first_token_ms=StatusDistributionSummary.from_values_function(
                function=lambda req: req.time_to_first_token_ms or 0.0,
                successful=accumulator.completed.get_within_range(
                    start_time,
                    end_time,
                    end_func=lambda req: req.first_token_iteration,
                ),
                incomplete=accumulator.incomplete.get_within_range(
                    start_time,
                    end_time,
                    end_func=lambda req: req.first_token_iteration,
                ),
                errored=accumulator.errored.get_within_range(
                    start_time,
                    end_time,
                    end_func=lambda req: req.first_token_iteration,
                ),
            ),
            time_to_first_output_token_ms=StatusDistributionSummary.from_values_function(
                function=lambda req: req.time_to_first_output_token_ms or 0.0,
                successful=accumulator.completed.get_within_range(
                    start_time,
                    end_time,
                    end_func=lambda req: req.first_output_token_iteration,
                ),
                incomplete=accumulator.incomplete.get_within_range(
                    start_time,
                    end_time,
                    end_func=lambda req: req.first_output_token_iteration,
                ),
                errored=accumulator.errored.get_within_range(
                    start_time,
                    end_time,
                    end_func=lambda req: req.first_output_token_iteration,
                ),
            ),
            time_per_output_token_ms=StatusDistributionSummary.from_values_function(
                function=lambda req: (
                    req.time_per_output_token_ms or 0.0,
                    req.output_tokens or 0.0,
                ),
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            inter_token_latency_ms=StatusDistributionSummary.from_values_function(
                function=lambda req: (
                    req.inter_token_latency_ms or 0.0,
                    (req.output_tokens or 1.0) - 1.0,
                ),
                successful=accumulator.completed.get_within_range(
                    start_time,
                    end_time,
                    start_func=lambda req: req.first_token_iteration,
                ),
                incomplete=accumulator.incomplete.get_within_range(
                    start_time,
                    end_time,
                    start_func=lambda req: req.first_token_iteration,
                ),
                errored=accumulator.errored.get_within_range(
                    start_time,
                    end_time,
                    start_func=lambda req: req.first_token_iteration,
                ),
            ),
            prompt_tokens_per_second=StatusDistributionSummary.rate_distribution_from_timings_function(
                function=lambda req: req.prompt_tokens_timing,
                successful=successful,
                incomplete=incomplete,
                errored=errored,
                start_time=start_time,
                end_time=end_time,
            ),
            output_tokens_per_second=StatusDistributionSummary.rate_distribution_from_timings_function(
                function=lambda req: req.output_tokens_timings,
                successful=successful,
                incomplete=incomplete,
                errored=errored,
                start_time=start_time,
                end_time=end_time,
            ),
            tokens_per_second=StatusDistributionSummary.rate_distribution_from_timings_function(
                function=lambda req: req.total_tokens_timings,
                successful=successful,
                incomplete=incomplete,
                errored=errored,
                start_time=start_time,
                end_time=end_time,
            ),
            output_tokens_per_iteration=StatusDistributionSummary.from_values_function(
                function=lambda req: [
                    tokens for (_timing, tokens) in req.output_tokens_timings
                ],
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            iter_tokens_per_iteration=StatusDistributionSummary.from_values_function(
                function=lambda req: [
                    tokens for (_timing, tokens) in req.iter_tokens_timings
                ],
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            # Domain-specific stats
            text=GenerativeTextMetricsSummary.compile(
                successful=successful, incomplete=incomplete, errored=errored
            ),
            image=GenerativeImageMetricsSummary.compile(
                successful=successful, incomplete=incomplete, errored=errored
            ),
            video=GenerativeVideoMetricsSummary.compile(
                successful=successful, incomplete=incomplete, errored=errored
            ),
            audio=GenerativeAudioMetricsSummary.compile(
                successful=successful, incomplete=incomplete, errored=errored
            ),
            tool_call=GenerativeToolCallMetricsSummary.compile(
                successful=successful, incomplete=incomplete, errored=errored
            ),
        )

compile(accumulator) classmethod

Compile comprehensive generative metrics from benchmark accumulator.

Parameters:

Name Type Description Default
accumulator GenerativeBenchmarkAccumulator

Benchmark accumulator with completed request statistics

required

Returns:

Type Description
GenerativeMetrics

Compiled generative metrics with all distributions and summaries

Raises:

Type Description
ValueError

If measure_start and measure_end/request_end are not set

Source code in src/guidellm/benchmark/schemas/metrics.py
@classmethod
def compile(cls, accumulator: GenerativeBenchmarkAccumulator) -> GenerativeMetrics:
    """
    Compile comprehensive generative metrics from benchmark accumulator.

    :param accumulator: Benchmark accumulator with completed request statistics
    :return: Compiled generative metrics with all distributions and summaries
    :raises ValueError: If measure_start and measure_end/request_end are not set
    """
    start_time = accumulator.timings.finalized_measure_start
    end_time = accumulator.timings.finalized_measure_end

    if start_time == -1.0 or end_time == -1.0:
        raise ValueError(
            "Cannot compile GenerativeMetrics: "
            "No measurement start or end times available."
        )

    successful = accumulator.completed.get_within_range(start_time, end_time)
    incomplete = accumulator.incomplete.get_within_range(start_time, end_time)
    errored = accumulator.errored.get_within_range(start_time, end_time)

    return GenerativeMetrics(
        # Request stats
        request_totals=StatusBreakdown(
            successful=len(successful),
            incomplete=len(incomplete),
            errored=len(errored),
            total=(len(successful) + len(incomplete) + len(errored)),
        ),
        requests_per_second=StatusDistributionSummary.rate_distribution_from_timings_function(
            function=lambda req: req.request_end_time,
            successful=successful,
            incomplete=incomplete,
            errored=errored,
            start_time=start_time,
            end_time=end_time,
        ),
        request_concurrency=StatusDistributionSummary.concurrency_distribution_from_timings_function(
            function=(
                lambda req: (
                    (req.request_start_time, req.request_end_time)
                    if req.request_start_time is not None
                    and req.request_end_time is not None
                    else None
                )
            ),
            successful=successful,
            incomplete=incomplete,
            errored=errored,
            start_time=start_time,
            end_time=end_time,
        ),
        request_latency=StatusDistributionSummary.from_values_function(
            function=lambda req: req.request_latency or 0.0,
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        request_streaming_iterations_count=StatusDistributionSummary.from_values_function(
            function=lambda req: req.info.timings.request_iterations or 0.0,
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        # General token stats
        prompt_token_count=StatusDistributionSummary.from_values_function(
            function=lambda req: req.prompt_tokens or 0.0,
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        output_token_count=StatusDistributionSummary.from_values_function(
            function=lambda req: req.output_tokens or 0.0,
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        total_token_count=StatusDistributionSummary.from_values_function(
            function=lambda req: req.total_tokens or 0.0,
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        # TODO: Need to evaluate closed=False vs closed=True for first-token
        # latencies. See github.com/vllm-project/guidellm/issues/1078
        time_to_first_token_ms=StatusDistributionSummary.from_values_function(
            function=lambda req: req.time_to_first_token_ms or 0.0,
            successful=accumulator.completed.get_within_range(
                start_time,
                end_time,
                end_func=lambda req: req.first_token_iteration,
            ),
            incomplete=accumulator.incomplete.get_within_range(
                start_time,
                end_time,
                end_func=lambda req: req.first_token_iteration,
            ),
            errored=accumulator.errored.get_within_range(
                start_time,
                end_time,
                end_func=lambda req: req.first_token_iteration,
            ),
        ),
        time_to_first_output_token_ms=StatusDistributionSummary.from_values_function(
            function=lambda req: req.time_to_first_output_token_ms or 0.0,
            successful=accumulator.completed.get_within_range(
                start_time,
                end_time,
                end_func=lambda req: req.first_output_token_iteration,
            ),
            incomplete=accumulator.incomplete.get_within_range(
                start_time,
                end_time,
                end_func=lambda req: req.first_output_token_iteration,
            ),
            errored=accumulator.errored.get_within_range(
                start_time,
                end_time,
                end_func=lambda req: req.first_output_token_iteration,
            ),
        ),
        time_per_output_token_ms=StatusDistributionSummary.from_values_function(
            function=lambda req: (
                req.time_per_output_token_ms or 0.0,
                req.output_tokens or 0.0,
            ),
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        inter_token_latency_ms=StatusDistributionSummary.from_values_function(
            function=lambda req: (
                req.inter_token_latency_ms or 0.0,
                (req.output_tokens or 1.0) - 1.0,
            ),
            successful=accumulator.completed.get_within_range(
                start_time,
                end_time,
                start_func=lambda req: req.first_token_iteration,
            ),
            incomplete=accumulator.incomplete.get_within_range(
                start_time,
                end_time,
                start_func=lambda req: req.first_token_iteration,
            ),
            errored=accumulator.errored.get_within_range(
                start_time,
                end_time,
                start_func=lambda req: req.first_token_iteration,
            ),
        ),
        prompt_tokens_per_second=StatusDistributionSummary.rate_distribution_from_timings_function(
            function=lambda req: req.prompt_tokens_timing,
            successful=successful,
            incomplete=incomplete,
            errored=errored,
            start_time=start_time,
            end_time=end_time,
        ),
        output_tokens_per_second=StatusDistributionSummary.rate_distribution_from_timings_function(
            function=lambda req: req.output_tokens_timings,
            successful=successful,
            incomplete=incomplete,
            errored=errored,
            start_time=start_time,
            end_time=end_time,
        ),
        tokens_per_second=StatusDistributionSummary.rate_distribution_from_timings_function(
            function=lambda req: req.total_tokens_timings,
            successful=successful,
            incomplete=incomplete,
            errored=errored,
            start_time=start_time,
            end_time=end_time,
        ),
        output_tokens_per_iteration=StatusDistributionSummary.from_values_function(
            function=lambda req: [
                tokens for (_timing, tokens) in req.output_tokens_timings
            ],
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        iter_tokens_per_iteration=StatusDistributionSummary.from_values_function(
            function=lambda req: [
                tokens for (_timing, tokens) in req.iter_tokens_timings
            ],
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        # Domain-specific stats
        text=GenerativeTextMetricsSummary.compile(
            successful=successful, incomplete=incomplete, errored=errored
        ),
        image=GenerativeImageMetricsSummary.compile(
            successful=successful, incomplete=incomplete, errored=errored
        ),
        video=GenerativeVideoMetricsSummary.compile(
            successful=successful, incomplete=incomplete, errored=errored
        ),
        audio=GenerativeAudioMetricsSummary.compile(
            successful=successful, incomplete=incomplete, errored=errored
        ),
        tool_call=GenerativeToolCallMetricsSummary.compile(
            successful=successful, incomplete=incomplete, errored=errored
        ),
    )

GenerativeMetricsAccumulator

Bases: StandardBaseModel

Accumulates generative model performance metrics during execution.

Tracks token throughput, latency characteristics, and request timing for generative workloads. Maintains running statistics for input/output tokens, time-to-first-token, inter-token latency, and streaming patterns for comprehensive performance analysis.

Source code in src/guidellm/benchmark/schemas/accumulator.py
class GenerativeMetricsAccumulator(StandardBaseModel):
    """
    Accumulates generative model performance metrics during execution.

    Tracks token throughput, latency characteristics, and request timing for generative
    workloads. Maintains running statistics for input/output tokens,
    time-to-first-token, inter-token latency, and streaming patterns for comprehensive
    performance analysis.
    """

    requests: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Accumulated request count statistics",
    )
    request_latency: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Accumulated request latency statistics",
    )
    prompt_tokens: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Accumulated input token count statistics",
    )
    output_tokens: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Accumulated output token count statistics",
    )
    total_tokens: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Accumulated total token count statistics",
    )
    time_to_first_token_ms: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Accumulated time to first token statistics in milliseconds",
    )
    time_to_first_output_token_ms: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Accumulated time to first content token stats in ms",
    )
    time_per_output_token_ms: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Accumulated time per output token statistics in milliseconds",
    )
    inter_token_latency_ms: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Accumulated inter-token latency statistics in milliseconds",
    )
    streaming_iterations: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Accumulated streaming iteration count statistics",
    )
    output_tokens_by_iteration: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Accumulated output tokens per iteration statistics",
    )
    iter_tokens_by_iteration: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Accumulated iteration tokens per iteration statistics",
    )

    def update_estimate(self, stats: GenerativeRequestStats, duration: float):
        """
        Update generative metrics with completed request statistics.

        Incorporates token counts, latency measurements, and streaming characteristics
        from a completed request into running metric accumulators with time-weighted
        calculations.

        :param stats: Request statistics containing token and latency measurements
        :param duration: Current benchmark duration for time-weighted metrics
        """
        self.requests.update_estimate(1.0, duration=duration)
        self.prompt_tokens.update_estimate(stats.prompt_tokens, duration=duration)
        self.output_tokens.update_estimate(stats.output_tokens, duration=duration)
        self.total_tokens.update_estimate(stats.total_tokens, duration=duration)
        self.request_latency.update_estimate(stats.request_latency, duration=duration)
        self.time_to_first_token_ms.update_estimate(
            stats.time_to_first_token_ms, duration=duration
        )
        self.time_to_first_output_token_ms.update_estimate(
            stats.time_to_first_output_token_ms, duration=duration
        )
        self.time_per_output_token_ms.update_estimate(
            stats.time_per_output_token_ms,
            count=int(stats.output_tokens or 0),
            duration=duration,
        )
        self.inter_token_latency_ms.update_estimate(
            stats.inter_token_latency_ms,
            count=int((stats.output_tokens or 1) - 1),
            duration=duration,
        )
        self.streaming_iterations.update_estimate(
            stats.token_iterations, duration=duration
        )
        self.output_tokens_by_iteration.update_estimate(
            stats.output_tokens_per_iteration,
            count=int(stats.token_iterations or 0),
            duration=duration,
        )
        self.iter_tokens_by_iteration.update_estimate(
            stats.iter_tokens_per_iteration,
            count=int((stats.token_iterations or 1) - 1),
            duration=duration,
        )

update_estimate(stats, duration)

Update generative metrics with completed request statistics.

Incorporates token counts, latency measurements, and streaming characteristics from a completed request into running metric accumulators with time-weighted calculations.

Parameters:

Name Type Description Default
stats GenerativeRequestStats

Request statistics containing token and latency measurements

required
duration float

Current benchmark duration for time-weighted metrics

required
Source code in src/guidellm/benchmark/schemas/accumulator.py
def update_estimate(self, stats: GenerativeRequestStats, duration: float):
    """
    Update generative metrics with completed request statistics.

    Incorporates token counts, latency measurements, and streaming characteristics
    from a completed request into running metric accumulators with time-weighted
    calculations.

    :param stats: Request statistics containing token and latency measurements
    :param duration: Current benchmark duration for time-weighted metrics
    """
    self.requests.update_estimate(1.0, duration=duration)
    self.prompt_tokens.update_estimate(stats.prompt_tokens, duration=duration)
    self.output_tokens.update_estimate(stats.output_tokens, duration=duration)
    self.total_tokens.update_estimate(stats.total_tokens, duration=duration)
    self.request_latency.update_estimate(stats.request_latency, duration=duration)
    self.time_to_first_token_ms.update_estimate(
        stats.time_to_first_token_ms, duration=duration
    )
    self.time_to_first_output_token_ms.update_estimate(
        stats.time_to_first_output_token_ms, duration=duration
    )
    self.time_per_output_token_ms.update_estimate(
        stats.time_per_output_token_ms,
        count=int(stats.output_tokens or 0),
        duration=duration,
    )
    self.inter_token_latency_ms.update_estimate(
        stats.inter_token_latency_ms,
        count=int((stats.output_tokens or 1) - 1),
        duration=duration,
    )
    self.streaming_iterations.update_estimate(
        stats.token_iterations, duration=duration
    )
    self.output_tokens_by_iteration.update_estimate(
        stats.output_tokens_per_iteration,
        count=int(stats.token_iterations or 0),
        duration=duration,
    )
    self.iter_tokens_by_iteration.update_estimate(
        stats.iter_tokens_per_iteration,
        count=int((stats.token_iterations or 1) - 1),
        duration=duration,
    )

GenerativeMetricsArgs

Bases: MetricsArgs

Metrics configuration for generative (autoregressive) benchmarks.

Source code in src/guidellm/benchmark/schemas/entrypoints.py
@MetricsArgs.register("generative")
class GenerativeMetricsArgs(MetricsArgs):
    """Metrics configuration for generative (autoregressive) benchmarks."""

    kind: Literal["generative"] = Field(
        default="generative",
        description="The kind of metrics configuration to use.",
    )
    sample_size: int | None = Field(
        default=None,
        description=(
            "Maximum number of requests per status group (completed, errored, "
            "incomplete) to retain full data (prompt, output, tool calls) for in "
            "the final benchmark. Lightweight stats (latency, token counts) are "
            "always kept for every request. None keeps all request data, 0 strips "
            "all request data, N > 0 uses reservoir sampling to retain N per group."
        ),
        examples=[None, 0, 100],
    )
    prefer_response_metrics: bool = Field(
        default=True,
        description=(
            "Prioritize server-reported metrics over client-calculated metrics "
            "when both are available."
        ),
    )

GenerativeMetricsSummary

Bases: StandardBaseDict

Statistical summaries for input, output, and total metrics.

Provides distribution summaries across successful, incomplete, and errored requests for absolute values, per-second rates, and concurrency levels.

Source code in src/guidellm/benchmark/schemas/metrics.py
class GenerativeMetricsSummary(StandardBaseDict):
    """
    Statistical summaries for input, output, and total metrics.

    Provides distribution summaries across successful, incomplete, and errored
    requests for absolute values, per-second rates, and concurrency levels.
    """

    input: StatusDistributionSummary | None = Field(
        description="Distribution of input metric values"
    )
    input_per_second: StatusDistributionSummary | None = Field(
        description="Distribution of input metric rates per second"
    )
    input_concurrency: StatusDistributionSummary | None = Field(
        description="Distribution of concurrent input metric values"
    )

    output: StatusDistributionSummary | None = Field(
        description="Distribution of output metric values"
    )
    output_per_second: StatusDistributionSummary | None = Field(
        description="Distribution of output metric rates per second"
    )
    output_concurrency: StatusDistributionSummary | None = Field(
        description="Distribution of concurrent output metric values"
    )

    total: StatusDistributionSummary | None = Field(
        description="Distribution of total metric values (input + output)"
    )
    total_per_second: StatusDistributionSummary | None = Field(
        description="Distribution of total metric rates per second"
    )
    total_concurrency: StatusDistributionSummary | None = Field(
        description="Distribution of concurrent total metric values"
    )

    @classmethod
    def compile(
        cls,
        property_name: str,
        successful: list[GenerativeRequestStats],
        incomplete: list[GenerativeRequestStats],
        errored: list[GenerativeRequestStats],
    ) -> GenerativeMetricsSummary | None:
        """
        Compile metrics summary from request statistics for a specific property.

        :param property_name: Name of the property to extract from request metrics
        :param successful: Successfully completed request statistics
        :param incomplete: Incomplete or cancelled request statistics
        :param errored: Failed request statistics
        :return: Compiled metrics summary or None if no data available
        """
        successful_metrics = cls.extract_property_metrics_for_summary(
            successful, property_name
        )
        incomplete_metrics = cls.extract_property_metrics_for_summary(
            incomplete, property_name
        )
        errored_metrics = cls.extract_property_metrics_for_summary(
            errored, property_name
        )

        return cls.compile_timed_metrics(
            successful=successful_metrics,
            incomplete=incomplete_metrics,
            errored=errored_metrics,
        )

    @classmethod
    def compile_timed_metrics(
        cls,
        successful: list[TimedMetricTypeAlias],
        incomplete: list[TimedMetricTypeAlias],
        errored: list[TimedMetricTypeAlias],
    ) -> GenerativeMetricsSummary | None:
        """
        Compile metrics summary from timed metric tuples.

        :param successful: Timed metrics from successful requests
        :param incomplete: Timed metrics from incomplete requests
        :param errored: Timed metrics from errored requests
        :return: Compiled metrics summary or None if no data available
        """

        def _compile_metric_distributions(
            metrics_by_status: dict[StatusTypes, list[TimedMetricTypeAlias]],
            value_index: int,
        ) -> tuple[
            StatusDistributionSummary | None,
            StatusDistributionSummary | None,
            StatusDistributionSummary | None,
            dict[StatusTypes, list[float]],
            dict[StatusTypes, list[tuple[float, float]]],
            dict[StatusTypes, list[tuple[float, float, float]]],
        ]:
            """Helper to compile value, rate, and concurrency distributions."""
            # Filter out None values instead of coercing to 0.0 so we can
            # distinguish "metric not applicable" (None) from "metric is
            # zero" (e.g. errored tool call requests with tool_call_count=0).
            value_lists: dict[StatusTypes, list[float]] = {
                status: [
                    float(val)
                    for metric in metrics
                    if metric is not None
                    for val in [metric[value_index]]
                    if val is not None
                ]
                for status, metrics in metrics_by_status.items()
            }

            # No data at all for this value index — skip distributions.
            if all(len(vl) == 0 for vl in value_lists.values()):
                return None, None, None, value_lists, {}, {}

            value_dist = StatusDistributionSummary.from_values(
                successful=value_lists["successful"],
                incomplete=value_lists["incomplete"],
                errored=value_lists["errored"],
            )

            rate_lists: dict[StatusTypes, list[tuple[float, float]]] = {
                status: [
                    (  # type: ignore[misc]
                        metric[_TIMED_METRIC_END_TIME_INDEX],
                        float(metric[value_index] or 0.0),
                    )
                    for metric in metrics
                    if metric is not None
                ]
                for status, metrics in metrics_by_status.items()
            }
            rate_dist = StatusDistributionSummary.rate_distribution_from_timings(
                successful=rate_lists["successful"],
                incomplete=rate_lists["incomplete"],
                errored=rate_lists["errored"],
            )

            concurrency_lists: dict[StatusTypes, list[tuple[float, float, float]]] = {
                status: [
                    (  # type: ignore[misc]
                        metric[_TIMED_METRIC_START_TIME_INDEX],
                        metric[_TIMED_METRIC_END_TIME_INDEX],
                        float(metric[value_index] or 0.0),
                    )
                    for metric in metrics
                    if metric is not None
                ]
                for status, metrics in metrics_by_status.items()
            }
            concurrency_dist = (
                StatusDistributionSummary.concurrency_distribution_from_timings(
                    successful=concurrency_lists["successful"],
                    incomplete=concurrency_lists["incomplete"],
                    errored=concurrency_lists["errored"],
                )
            )

            return (
                value_dist,
                rate_dist,
                concurrency_dist,
                value_lists,
                rate_lists,
                concurrency_lists,
            )

        metrics_by_status: dict[StatusTypes, list[TimedMetricTypeAlias]] = {
            "successful": successful,
            "incomplete": incomplete,
            "errored": errored,
        }

        # Calculate input distributions
        (
            input_value_dist,
            input_rate_dist,
            input_concurrency_dist,
            input_value_lists,
            input_rate_lists,
            input_concurrency_lists,
        ) = _compile_metric_distributions(
            metrics_by_status, _TIMED_METRIC_INPUT_VALUE_INDEX
        )

        # Calculate output distributions
        (
            output_value_dist,
            output_rate_dist,
            output_concurrency_dist,
            output_value_lists,
            output_rate_lists,
            output_concurrency_lists,
        ) = _compile_metric_distributions(
            metrics_by_status, _TIMED_METRIC_OUTPUT_VALUE_INDEX
        )

        # Calculate total distributions if both input and output have data
        if input_value_dist is not None and output_value_dist is not None:
            total_value_dist = StatusDistributionSummary.from_values(
                successful=(
                    input_value_lists["successful"] + output_value_lists["successful"]
                ),
                incomplete=(
                    input_value_lists["incomplete"] + output_value_lists["incomplete"]
                ),
                errored=input_value_lists["errored"] + output_value_lists["errored"],
            )
            total_rate_dist = StatusDistributionSummary.rate_distribution_from_timings(
                successful=(
                    input_rate_lists["successful"] + output_rate_lists["successful"]
                ),
                incomplete=(
                    input_rate_lists["incomplete"] + output_rate_lists["incomplete"]
                ),
                errored=input_rate_lists["errored"] + output_rate_lists["errored"],
            )
            total_concurrency_dist = (
                StatusDistributionSummary.concurrency_distribution_from_timings(
                    successful=(
                        input_concurrency_lists["successful"]
                        + output_concurrency_lists["successful"]
                    ),
                    incomplete=(
                        input_concurrency_lists["incomplete"]
                        + output_concurrency_lists["incomplete"]
                    ),
                    errored=(
                        input_concurrency_lists["errored"]
                        + output_concurrency_lists["errored"]
                    ),
                )
            )
        else:
            total_value_dist = None
            total_rate_dist = None
            total_concurrency_dist = None

        return GenerativeMetricsSummary(
            input=input_value_dist,
            input_per_second=input_rate_dist,
            input_concurrency=input_concurrency_dist,
            output=output_value_dist,
            output_per_second=output_rate_dist,
            output_concurrency=output_concurrency_dist,
            total=total_value_dist,
            total_per_second=total_rate_dist,
            total_concurrency=total_concurrency_dist,
        )

    @classmethod
    def extract_property_metrics_for_summary(
        cls, stats_list: list[GenerativeRequestStats], property_name: str
    ) -> list[TimedMetricTypeAlias]:
        """
        Extract timed metrics for a specific property from request statistics.

        :param stats_list: List of request statistics to extract from
        :param property_name: Name of the property to extract from metrics
        :return: List of tuples containing
            (start_time, end_time, input_value, output_value)
        """
        return [
            (
                stats.request_start_time,
                stats.request_end_time,
                getattr(stats.input_metrics, property_name),
                getattr(stats.output_metrics, property_name),
            )
            for stats in stats_list
            if (
                stats.request_start_time
                and stats.request_end_time
                and (
                    getattr(stats.input_metrics, property_name) is not None
                    or getattr(stats.output_metrics, property_name) is not None
                )
            )
        ]

compile(property_name, successful, incomplete, errored) classmethod

Compile metrics summary from request statistics for a specific property.

Parameters:

Name Type Description Default
property_name str

Name of the property to extract from request metrics

required
successful list[GenerativeRequestStats]

Successfully completed request statistics

required
incomplete list[GenerativeRequestStats]

Incomplete or cancelled request statistics

required
errored list[GenerativeRequestStats]

Failed request statistics

required

Returns:

Type Description
GenerativeMetricsSummary | None

Compiled metrics summary or None if no data available

Source code in src/guidellm/benchmark/schemas/metrics.py
@classmethod
def compile(
    cls,
    property_name: str,
    successful: list[GenerativeRequestStats],
    incomplete: list[GenerativeRequestStats],
    errored: list[GenerativeRequestStats],
) -> GenerativeMetricsSummary | None:
    """
    Compile metrics summary from request statistics for a specific property.

    :param property_name: Name of the property to extract from request metrics
    :param successful: Successfully completed request statistics
    :param incomplete: Incomplete or cancelled request statistics
    :param errored: Failed request statistics
    :return: Compiled metrics summary or None if no data available
    """
    successful_metrics = cls.extract_property_metrics_for_summary(
        successful, property_name
    )
    incomplete_metrics = cls.extract_property_metrics_for_summary(
        incomplete, property_name
    )
    errored_metrics = cls.extract_property_metrics_for_summary(
        errored, property_name
    )

    return cls.compile_timed_metrics(
        successful=successful_metrics,
        incomplete=incomplete_metrics,
        errored=errored_metrics,
    )

compile_timed_metrics(successful, incomplete, errored) classmethod

Compile metrics summary from timed metric tuples.

Parameters:

Name Type Description Default
successful list[TimedMetricTypeAlias]

Timed metrics from successful requests

required
incomplete list[TimedMetricTypeAlias]

Timed metrics from incomplete requests

required
errored list[TimedMetricTypeAlias]

Timed metrics from errored requests

required

Returns:

Type Description
GenerativeMetricsSummary | None

Compiled metrics summary or None if no data available

Source code in src/guidellm/benchmark/schemas/metrics.py
@classmethod
def compile_timed_metrics(
    cls,
    successful: list[TimedMetricTypeAlias],
    incomplete: list[TimedMetricTypeAlias],
    errored: list[TimedMetricTypeAlias],
) -> GenerativeMetricsSummary | None:
    """
    Compile metrics summary from timed metric tuples.

    :param successful: Timed metrics from successful requests
    :param incomplete: Timed metrics from incomplete requests
    :param errored: Timed metrics from errored requests
    :return: Compiled metrics summary or None if no data available
    """

    def _compile_metric_distributions(
        metrics_by_status: dict[StatusTypes, list[TimedMetricTypeAlias]],
        value_index: int,
    ) -> tuple[
        StatusDistributionSummary | None,
        StatusDistributionSummary | None,
        StatusDistributionSummary | None,
        dict[StatusTypes, list[float]],
        dict[StatusTypes, list[tuple[float, float]]],
        dict[StatusTypes, list[tuple[float, float, float]]],
    ]:
        """Helper to compile value, rate, and concurrency distributions."""
        # Filter out None values instead of coercing to 0.0 so we can
        # distinguish "metric not applicable" (None) from "metric is
        # zero" (e.g. errored tool call requests with tool_call_count=0).
        value_lists: dict[StatusTypes, list[float]] = {
            status: [
                float(val)
                for metric in metrics
                if metric is not None
                for val in [metric[value_index]]
                if val is not None
            ]
            for status, metrics in metrics_by_status.items()
        }

        # No data at all for this value index — skip distributions.
        if all(len(vl) == 0 for vl in value_lists.values()):
            return None, None, None, value_lists, {}, {}

        value_dist = StatusDistributionSummary.from_values(
            successful=value_lists["successful"],
            incomplete=value_lists["incomplete"],
            errored=value_lists["errored"],
        )

        rate_lists: dict[StatusTypes, list[tuple[float, float]]] = {
            status: [
                (  # type: ignore[misc]
                    metric[_TIMED_METRIC_END_TIME_INDEX],
                    float(metric[value_index] or 0.0),
                )
                for metric in metrics
                if metric is not None
            ]
            for status, metrics in metrics_by_status.items()
        }
        rate_dist = StatusDistributionSummary.rate_distribution_from_timings(
            successful=rate_lists["successful"],
            incomplete=rate_lists["incomplete"],
            errored=rate_lists["errored"],
        )

        concurrency_lists: dict[StatusTypes, list[tuple[float, float, float]]] = {
            status: [
                (  # type: ignore[misc]
                    metric[_TIMED_METRIC_START_TIME_INDEX],
                    metric[_TIMED_METRIC_END_TIME_INDEX],
                    float(metric[value_index] or 0.0),
                )
                for metric in metrics
                if metric is not None
            ]
            for status, metrics in metrics_by_status.items()
        }
        concurrency_dist = (
            StatusDistributionSummary.concurrency_distribution_from_timings(
                successful=concurrency_lists["successful"],
                incomplete=concurrency_lists["incomplete"],
                errored=concurrency_lists["errored"],
            )
        )

        return (
            value_dist,
            rate_dist,
            concurrency_dist,
            value_lists,
            rate_lists,
            concurrency_lists,
        )

    metrics_by_status: dict[StatusTypes, list[TimedMetricTypeAlias]] = {
        "successful": successful,
        "incomplete": incomplete,
        "errored": errored,
    }

    # Calculate input distributions
    (
        input_value_dist,
        input_rate_dist,
        input_concurrency_dist,
        input_value_lists,
        input_rate_lists,
        input_concurrency_lists,
    ) = _compile_metric_distributions(
        metrics_by_status, _TIMED_METRIC_INPUT_VALUE_INDEX
    )

    # Calculate output distributions
    (
        output_value_dist,
        output_rate_dist,
        output_concurrency_dist,
        output_value_lists,
        output_rate_lists,
        output_concurrency_lists,
    ) = _compile_metric_distributions(
        metrics_by_status, _TIMED_METRIC_OUTPUT_VALUE_INDEX
    )

    # Calculate total distributions if both input and output have data
    if input_value_dist is not None and output_value_dist is not None:
        total_value_dist = StatusDistributionSummary.from_values(
            successful=(
                input_value_lists["successful"] + output_value_lists["successful"]
            ),
            incomplete=(
                input_value_lists["incomplete"] + output_value_lists["incomplete"]
            ),
            errored=input_value_lists["errored"] + output_value_lists["errored"],
        )
        total_rate_dist = StatusDistributionSummary.rate_distribution_from_timings(
            successful=(
                input_rate_lists["successful"] + output_rate_lists["successful"]
            ),
            incomplete=(
                input_rate_lists["incomplete"] + output_rate_lists["incomplete"]
            ),
            errored=input_rate_lists["errored"] + output_rate_lists["errored"],
        )
        total_concurrency_dist = (
            StatusDistributionSummary.concurrency_distribution_from_timings(
                successful=(
                    input_concurrency_lists["successful"]
                    + output_concurrency_lists["successful"]
                ),
                incomplete=(
                    input_concurrency_lists["incomplete"]
                    + output_concurrency_lists["incomplete"]
                ),
                errored=(
                    input_concurrency_lists["errored"]
                    + output_concurrency_lists["errored"]
                ),
            )
        )
    else:
        total_value_dist = None
        total_rate_dist = None
        total_concurrency_dist = None

    return GenerativeMetricsSummary(
        input=input_value_dist,
        input_per_second=input_rate_dist,
        input_concurrency=input_concurrency_dist,
        output=output_value_dist,
        output_per_second=output_rate_dist,
        output_concurrency=output_concurrency_dist,
        total=total_value_dist,
        total_per_second=total_rate_dist,
        total_concurrency=total_concurrency_dist,
    )

extract_property_metrics_for_summary(stats_list, property_name) classmethod

Extract timed metrics for a specific property from request statistics.

Parameters:

Name Type Description Default
stats_list list[GenerativeRequestStats]

List of request statistics to extract from

required
property_name str

Name of the property to extract from metrics

required

Returns:

Type Description
list[TimedMetricTypeAlias]

List of tuples containing (start_time, end_time, input_value, output_value)

Source code in src/guidellm/benchmark/schemas/metrics.py
@classmethod
def extract_property_metrics_for_summary(
    cls, stats_list: list[GenerativeRequestStats], property_name: str
) -> list[TimedMetricTypeAlias]:
    """
    Extract timed metrics for a specific property from request statistics.

    :param stats_list: List of request statistics to extract from
    :param property_name: Name of the property to extract from metrics
    :return: List of tuples containing
        (start_time, end_time, input_value, output_value)
    """
    return [
        (
            stats.request_start_time,
            stats.request_end_time,
            getattr(stats.input_metrics, property_name),
            getattr(stats.output_metrics, property_name),
        )
        for stats in stats_list
        if (
            stats.request_start_time
            and stats.request_end_time
            and (
                getattr(stats.input_metrics, property_name) is not None
                or getattr(stats.output_metrics, property_name) is not None
            )
        )
    ]

GenerativeRequestsAccumulator

Bases: StandardBaseModel

Manages request statistics collection with optional reservoir sampling.

Collects detailed request statistics while optionally sampling to limit memory usage in long-running benchmarks. Supports configurable sampling rates and selective data retention (clearing request arguments and/or outputs for non-sampled requests).

Source code in src/guidellm/benchmark/schemas/accumulator.py
class GenerativeRequestsAccumulator(StandardBaseModel):
    """
    Manages request statistics collection with optional reservoir sampling.

    Collects detailed request statistics while optionally sampling to limit memory usage
    in long-running benchmarks. Supports configurable sampling rates and selective data
    retention (clearing request arguments and/or outputs for non-sampled requests).
    """

    sample_size: int | None = Field(
        default=None,
        description=(
            "Maximum number of requests in each status group to retain full data "
            "(prompt, output, tool calls) for. Lightweight stats are always kept. "
            "None keeps all, 0 strips all, N > 0 uses reservoir sampling."
        ),
    )
    requests_stats: list[GenerativeRequestStats] = Field(
        description="List of generative request statistics", default_factory=list
    )
    samples: list[int] | None = Field(
        description="Indices of sampled generative requests", default=None
    )
    clear_nonsampled_request_args: bool = Field(
        default=True,
        description=(
            "Whether to clear request arguments and outputs for non-sampled requests"
        ),
    )
    clear_nonsampled_outputs: bool = Field(
        default=True,
        description=(
            "Whether to clear outputs for non-sampled requests while keeping args"
        ),
    )

    def get_sampled(self) -> list[GenerativeRequestStats]:
        """
        Retrieve the list of sampled request statistics.

        :return: List of sampled generative request statistics
        """
        if self.samples is None:
            return self.requests_stats

        return [self.requests_stats[ind] for ind in self.samples]

    def get_within_range(
        self,
        start_time: float,
        end_time: float,
        start_func: Callable[[GenerativeRequestStats], float | None] = lambda s: (
            s.request_start_time
        ),
        end_func: Callable[[GenerativeRequestStats], float | None] = lambda s: (
            s.request_end_time
        ),
        closed: bool = False,
    ) -> list[GenerativeRequestStats]:
        """
        Retrieve request statistics within a specified time range.

        If the request has no start time we treat it
        as an instantaneous event at the end time.

        :param start_time: Start timestamp for filtering (events must end after this)
        :param end_time: End timestamp for filtering (events must start before this)
        :param start_func: Function to extract the start timestamp from a request stat
        :param end_func: Function to extract the end timestamp from a request stats
        :param closed: If True, events must be strictly enclosed within the time range.
                       If False, include events that partially overlap the time range.
        :return: List of request statistics within the time range
        """
        within_range = []
        for stats in self.requests_stats:
            start_event = start_func(stats)
            end_event = end_func(stats)

            # Skip if we don't know when the event ended
            if end_event is None:
                continue

            # Treat instantaneous events as starting exactly when they end
            actual_start = start_event if start_event is not None else end_event

            if closed:
                # Fully enclosed
                is_in_range = actual_start >= start_time and end_event <= end_time
            else:
                # Partial overlap
                is_in_range = end_event >= start_time and actual_start <= end_time

            if is_in_range:
                within_range.append(stats)

        return within_range

    def update_estimate(
        self,
        response: GenerationResponse | None,
        request: GenerationRequest,
        info: RequestInfo,
        prefer_response_metrics: bool,
    ) -> GenerativeRequestStats:
        """
        Record request statistics and apply reservoir sampling if configured.

        Compiles statistics from the completed request and adds to the collection.
        Uses reservoir sampling algorithm to maintain uniform sample distribution when
        enabled, clearing non-sampled request data to manage memory.

        :param response: Generation response containing output and metrics
        :param request: Original generation request with input data
        :param info: Request execution information and timing
        :param prefer_response_metrics: Whether to prefer metrics from response
        :return: Compiled request statistics
        """
        stats = self.compile_stats(response, request, info, prefer_response_metrics)

        current_index = len(self.requests_stats)
        self.requests_stats.append(stats)

        if self.sample_size is None:
            # Keeping all requests, don't need to sample
            self.samples = None
        elif self.sample_size <= 0:
            # Not keeping any requests, clear out unnecessary memory usage for current
            self.clear_stats_data(stats)
        elif self.sample_size >= len(self.requests_stats):
            # Add directly to samples, haven't filled yet
            if self.samples is None:
                self.samples = []
            self.samples.append(current_index)
        elif self.sample_size / len(self.requests_stats) >= random.random():
            # Sampling logic: choose to replace with decreasing probability s / n
            # where s is sample size, n is current number of requests.
            # If chosen, choose random existing sample to replace.
            # P(new item in samples)  = s / n
            # P(prev item in samples) = P(item was in samples) * P(not replaced)
            # P(prev item in samples) =
            #    P(before replacement) * P(new item selected) * P(chosen from samples)
            # P(prev item in samples) = (s / (n - 1)) * (s / n) * (1 / s) = s / n
            # P(prev item in samples) = P(new item in samples)
            if self.samples is None:
                self.samples = []
            replace_index = random.randrange(len(self.samples))
            self.clear_stats_data(self.samples[replace_index])
            self.samples[replace_index] = current_index

        return stats

    def clear_stats_data(self, stats: GenerativeRequestStats | int):
        if isinstance(stats, int):
            stats = self.requests_stats[stats]

        if self.clear_nonsampled_request_args:
            stats.request_args = None
        if self.clear_nonsampled_outputs:
            stats.output = None
            stats.reasoning_output = None
            stats.tool_calls = None

    @classmethod
    def compile_stats(
        cls,
        response: GenerationResponse | None,
        request: GenerationRequest,
        info: RequestInfo,
        prefer_response_metrics: bool,
    ) -> GenerativeRequestStats:
        """
        Compile statistics from request, response, and execution info.

        :param response: Generation response with output and metrics, or None
        :param request: Original generation request with input data
        :param info: Request execution information and timing
        :param prefer_response_metrics: Whether to prefer metrics from response
        :return: Compiled generative request statistics
        """
        # Extract the first request for arguments if multi-turn
        first_request: GenerationRequest
        if isinstance(request, GenerationRequest):
            first_request = request
        else:
            # Multi-turn request: extract first item
            first_item = request[0]
            first_request = (
                first_item[0] if isinstance(first_item, tuple) else first_item
            )

        if response is None:
            response = GenerationResponse(
                request_id=info.request_id,
                request_args=None,
            )

        return response.compile_stats(
            request=first_request,
            info=info,
            prefer_response=prefer_response_metrics,
        )

compile_stats(response, request, info, prefer_response_metrics) classmethod

Compile statistics from request, response, and execution info.

Parameters:

Name Type Description Default
response GenerationResponse | None

Generation response with output and metrics, or None

required
request GenerationRequest

Original generation request with input data

required
info RequestInfo

Request execution information and timing

required
prefer_response_metrics bool

Whether to prefer metrics from response

required

Returns:

Type Description
GenerativeRequestStats

Compiled generative request statistics

Source code in src/guidellm/benchmark/schemas/accumulator.py
@classmethod
def compile_stats(
    cls,
    response: GenerationResponse | None,
    request: GenerationRequest,
    info: RequestInfo,
    prefer_response_metrics: bool,
) -> GenerativeRequestStats:
    """
    Compile statistics from request, response, and execution info.

    :param response: Generation response with output and metrics, or None
    :param request: Original generation request with input data
    :param info: Request execution information and timing
    :param prefer_response_metrics: Whether to prefer metrics from response
    :return: Compiled generative request statistics
    """
    # Extract the first request for arguments if multi-turn
    first_request: GenerationRequest
    if isinstance(request, GenerationRequest):
        first_request = request
    else:
        # Multi-turn request: extract first item
        first_item = request[0]
        first_request = (
            first_item[0] if isinstance(first_item, tuple) else first_item
        )

    if response is None:
        response = GenerationResponse(
            request_id=info.request_id,
            request_args=None,
        )

    return response.compile_stats(
        request=first_request,
        info=info,
        prefer_response=prefer_response_metrics,
    )

get_sampled()

Retrieve the list of sampled request statistics.

Returns:

Type Description
list[GenerativeRequestStats]

List of sampled generative request statistics

Source code in src/guidellm/benchmark/schemas/accumulator.py
def get_sampled(self) -> list[GenerativeRequestStats]:
    """
    Retrieve the list of sampled request statistics.

    :return: List of sampled generative request statistics
    """
    if self.samples is None:
        return self.requests_stats

    return [self.requests_stats[ind] for ind in self.samples]

get_within_range(start_time, end_time, start_func=lambda s: s.request_start_time, end_func=lambda s: s.request_end_time, closed=False)

Retrieve request statistics within a specified time range.

If the request has no start time we treat it as an instantaneous event at the end time.

Parameters:

Name Type Description Default
start_time float

Start timestamp for filtering (events must end after this)

required
end_time float

End timestamp for filtering (events must start before this)

required
start_func Callable[[GenerativeRequestStats], float | None]

Function to extract the start timestamp from a request stat

lambda s: request_start_time
end_func Callable[[GenerativeRequestStats], float | None]

Function to extract the end timestamp from a request stats

lambda s: request_end_time
closed bool

If True, events must be strictly enclosed within the time range. If False, include events that partially overlap the time range.

False

Returns:

Type Description
list[GenerativeRequestStats]

List of request statistics within the time range

Source code in src/guidellm/benchmark/schemas/accumulator.py
def get_within_range(
    self,
    start_time: float,
    end_time: float,
    start_func: Callable[[GenerativeRequestStats], float | None] = lambda s: (
        s.request_start_time
    ),
    end_func: Callable[[GenerativeRequestStats], float | None] = lambda s: (
        s.request_end_time
    ),
    closed: bool = False,
) -> list[GenerativeRequestStats]:
    """
    Retrieve request statistics within a specified time range.

    If the request has no start time we treat it
    as an instantaneous event at the end time.

    :param start_time: Start timestamp for filtering (events must end after this)
    :param end_time: End timestamp for filtering (events must start before this)
    :param start_func: Function to extract the start timestamp from a request stat
    :param end_func: Function to extract the end timestamp from a request stats
    :param closed: If True, events must be strictly enclosed within the time range.
                   If False, include events that partially overlap the time range.
    :return: List of request statistics within the time range
    """
    within_range = []
    for stats in self.requests_stats:
        start_event = start_func(stats)
        end_event = end_func(stats)

        # Skip if we don't know when the event ended
        if end_event is None:
            continue

        # Treat instantaneous events as starting exactly when they end
        actual_start = start_event if start_event is not None else end_event

        if closed:
            # Fully enclosed
            is_in_range = actual_start >= start_time and end_event <= end_time
        else:
            # Partial overlap
            is_in_range = end_event >= start_time and actual_start <= end_time

        if is_in_range:
            within_range.append(stats)

    return within_range

update_estimate(response, request, info, prefer_response_metrics)

Record request statistics and apply reservoir sampling if configured.

Compiles statistics from the completed request and adds to the collection. Uses reservoir sampling algorithm to maintain uniform sample distribution when enabled, clearing non-sampled request data to manage memory.

Parameters:

Name Type Description Default
response GenerationResponse | None

Generation response containing output and metrics

required
request GenerationRequest

Original generation request with input data

required
info RequestInfo

Request execution information and timing

required
prefer_response_metrics bool

Whether to prefer metrics from response

required

Returns:

Type Description
GenerativeRequestStats

Compiled request statistics

Source code in src/guidellm/benchmark/schemas/accumulator.py
def update_estimate(
    self,
    response: GenerationResponse | None,
    request: GenerationRequest,
    info: RequestInfo,
    prefer_response_metrics: bool,
) -> GenerativeRequestStats:
    """
    Record request statistics and apply reservoir sampling if configured.

    Compiles statistics from the completed request and adds to the collection.
    Uses reservoir sampling algorithm to maintain uniform sample distribution when
    enabled, clearing non-sampled request data to manage memory.

    :param response: Generation response containing output and metrics
    :param request: Original generation request with input data
    :param info: Request execution information and timing
    :param prefer_response_metrics: Whether to prefer metrics from response
    :return: Compiled request statistics
    """
    stats = self.compile_stats(response, request, info, prefer_response_metrics)

    current_index = len(self.requests_stats)
    self.requests_stats.append(stats)

    if self.sample_size is None:
        # Keeping all requests, don't need to sample
        self.samples = None
    elif self.sample_size <= 0:
        # Not keeping any requests, clear out unnecessary memory usage for current
        self.clear_stats_data(stats)
    elif self.sample_size >= len(self.requests_stats):
        # Add directly to samples, haven't filled yet
        if self.samples is None:
            self.samples = []
        self.samples.append(current_index)
    elif self.sample_size / len(self.requests_stats) >= random.random():
        # Sampling logic: choose to replace with decreasing probability s / n
        # where s is sample size, n is current number of requests.
        # If chosen, choose random existing sample to replace.
        # P(new item in samples)  = s / n
        # P(prev item in samples) = P(item was in samples) * P(not replaced)
        # P(prev item in samples) =
        #    P(before replacement) * P(new item selected) * P(chosen from samples)
        # P(prev item in samples) = (s / (n - 1)) * (s / n) * (1 / s) = s / n
        # P(prev item in samples) = P(new item in samples)
        if self.samples is None:
            self.samples = []
        replace_index = random.randrange(len(self.samples))
        self.clear_stats_data(self.samples[replace_index])
        self.samples[replace_index] = current_index

    return stats

GenerativeTextMetricsSummary

Bases: StandardBaseDict

Text-specific metric summaries for generative benchmarks.

Tracks token, word, and character-level metrics across input, output, and total usage for text generation workloads.

Source code in src/guidellm/benchmark/schemas/metrics.py
class GenerativeTextMetricsSummary(StandardBaseDict):
    """
    Text-specific metric summaries for generative benchmarks.

    Tracks token, word, and character-level metrics across input, output, and
    total usage for text generation workloads.
    """

    tokens: GenerativeMetricsSummary | None = Field(
        description="Token count metrics and distributions"
    )
    words: GenerativeMetricsSummary | None = Field(
        description="Word count metrics and distributions"
    )
    characters: GenerativeMetricsSummary | None = Field(
        description="Character count metrics and distributions"
    )

    @classmethod
    def compile(
        cls,
        successful: list[GenerativeRequestStats],
        incomplete: list[GenerativeRequestStats],
        errored: list[GenerativeRequestStats],
    ) -> GenerativeTextMetricsSummary:
        """
        Compile text metrics summary from request statistics.

        :param successful: Successfully completed request statistics
        :param incomplete: Incomplete/cancelled request statistics
        :param errored: Failed request statistics
        :return: Compiled text metrics summary
        """
        return GenerativeTextMetricsSummary(
            tokens=GenerativeMetricsSummary.compile(
                property_name="text_tokens",
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            words=GenerativeMetricsSummary.compile(
                property_name="text_words",
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            characters=GenerativeMetricsSummary.compile(
                property_name="text_characters",
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
        )

compile(successful, incomplete, errored) classmethod

Compile text metrics summary from request statistics.

Parameters:

Name Type Description Default
successful list[GenerativeRequestStats]

Successfully completed request statistics

required
incomplete list[GenerativeRequestStats]

Incomplete/cancelled request statistics

required
errored list[GenerativeRequestStats]

Failed request statistics

required

Returns:

Type Description
GenerativeTextMetricsSummary

Compiled text metrics summary

Source code in src/guidellm/benchmark/schemas/metrics.py
@classmethod
def compile(
    cls,
    successful: list[GenerativeRequestStats],
    incomplete: list[GenerativeRequestStats],
    errored: list[GenerativeRequestStats],
) -> GenerativeTextMetricsSummary:
    """
    Compile text metrics summary from request statistics.

    :param successful: Successfully completed request statistics
    :param incomplete: Incomplete/cancelled request statistics
    :param errored: Failed request statistics
    :return: Compiled text metrics summary
    """
    return GenerativeTextMetricsSummary(
        tokens=GenerativeMetricsSummary.compile(
            property_name="text_tokens",
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        words=GenerativeMetricsSummary.compile(
            property_name="text_words",
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        characters=GenerativeMetricsSummary.compile(
            property_name="text_characters",
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
    )

GenerativeVideoMetricsSummary

Bases: StandardBaseDict

Video-specific metric summaries for generative benchmarks.

Tracks token, frame count, duration, and byte-level metrics across input, output, and total usage for video generation workloads.

Source code in src/guidellm/benchmark/schemas/metrics.py
class GenerativeVideoMetricsSummary(StandardBaseDict):
    """
    Video-specific metric summaries for generative benchmarks.

    Tracks token, frame count, duration, and byte-level metrics across input,
    output, and total usage for video generation workloads.
    """

    tokens: GenerativeMetricsSummary | None = Field(
        description="Video token count metrics and distributions"
    )
    frames: GenerativeMetricsSummary | None = Field(
        description="Frame count metrics and distributions"
    )
    seconds: GenerativeMetricsSummary | None = Field(
        description="Duration metrics in seconds and distributions"
    )
    bytes: GenerativeMetricsSummary | None = Field(
        description="Byte size metrics and distributions"
    )

    @classmethod
    def compile(
        cls,
        successful: list[GenerativeRequestStats],
        incomplete: list[GenerativeRequestStats],
        errored: list[GenerativeRequestStats],
    ) -> GenerativeVideoMetricsSummary:
        """
        Compile video metrics summary from request statistics.

        :param successful: Successfully completed request statistics
        :param incomplete: Incomplete/cancelled request statistics
        :param errored: Failed request statistics
        :return: Compiled video metrics summary
        """
        return GenerativeVideoMetricsSummary(
            tokens=GenerativeMetricsSummary.compile(
                property_name="video_tokens",
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            frames=GenerativeMetricsSummary.compile(
                property_name="video_frames",
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            seconds=GenerativeMetricsSummary.compile(
                property_name="video_seconds",
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
            bytes=GenerativeMetricsSummary.compile(
                property_name="video_bytes",
                successful=successful,
                incomplete=incomplete,
                errored=errored,
            ),
        )

compile(successful, incomplete, errored) classmethod

Compile video metrics summary from request statistics.

Parameters:

Name Type Description Default
successful list[GenerativeRequestStats]

Successfully completed request statistics

required
incomplete list[GenerativeRequestStats]

Incomplete/cancelled request statistics

required
errored list[GenerativeRequestStats]

Failed request statistics

required

Returns:

Type Description
GenerativeVideoMetricsSummary

Compiled video metrics summary

Source code in src/guidellm/benchmark/schemas/metrics.py
@classmethod
def compile(
    cls,
    successful: list[GenerativeRequestStats],
    incomplete: list[GenerativeRequestStats],
    errored: list[GenerativeRequestStats],
) -> GenerativeVideoMetricsSummary:
    """
    Compile video metrics summary from request statistics.

    :param successful: Successfully completed request statistics
    :param incomplete: Incomplete/cancelled request statistics
    :param errored: Failed request statistics
    :return: Compiled video metrics summary
    """
    return GenerativeVideoMetricsSummary(
        tokens=GenerativeMetricsSummary.compile(
            property_name="video_tokens",
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        frames=GenerativeMetricsSummary.compile(
            property_name="video_frames",
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        seconds=GenerativeMetricsSummary.compile(
            property_name="video_seconds",
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
        bytes=GenerativeMetricsSummary.compile(
            property_name="video_bytes",
            successful=successful,
            incomplete=incomplete,
            errored=errored,
        ),
    )

MetricsArgs

Bases: PydanticClassRegistryMixin['MetricsArgs'], ABC

Base class for metrics collection arguments.

Attributes:

Name Type Description
schema_discriminator str

Field name for polymorphic deserialization

Source code in src/guidellm/benchmark/schemas/entrypoints.py
class MetricsArgs(PydanticClassRegistryMixin["MetricsArgs"], ABC):
    """Base class for metrics collection arguments.

    :cvar schema_discriminator: Field name for polymorphic deserialization
    """

    model_config = standard_model_config()

    schema_discriminator: ClassVar[str] = "kind"

    @classmethod
    def __pydantic_schema_base_type__(cls) -> type[MetricsArgs]:
        """
        Return base type for polymorphic validation hierarchy.

        :return: Base MetricsArgs class for schema validation
        """
        if cls.__name__ == "MetricsArgs":
            return cls

        return MetricsArgs

    kind: str = Field(
        description="The kind of metrics configuration to use.",
    )

__pydantic_schema_base_type__() classmethod

Return base type for polymorphic validation hierarchy.

Returns:

Type Description
type[MetricsArgs]

Base MetricsArgs class for schema validation

Source code in src/guidellm/benchmark/schemas/entrypoints.py
@classmethod
def __pydantic_schema_base_type__(cls) -> type[MetricsArgs]:
    """
    Return base type for polymorphic validation hierarchy.

    :return: Base MetricsArgs class for schema validation
    """
    if cls.__name__ == "MetricsArgs":
        return cls

    return MetricsArgs

ProfileArgs

Bases: PydanticClassRegistryMixin['ProfileArgs'], ABC

Base class for profile creation arguments.

This class serves as a base for defining argument models used in the creation of profile instances. It inherits from PydanticClassRegistryMixin to enable automatic registration of subclasses, allowing for flexible and extensible profile configurations.

Attributes:

Name Type Description
schema_discriminator str

Field name for polymorphic deserialization

Source code in src/guidellm/benchmark/schemas/profiles.py
class ProfileArgs(PydanticClassRegistryMixin["ProfileArgs"], ABC):
    """Base class for profile creation arguments.

    This class serves as a base for defining argument models used in the creation
    of profile instances. It inherits from PydanticClassRegistryMixin to enable
    automatic registration of subclasses, allowing for flexible and extensible
    profile configurations.

    :cvar schema_discriminator: Field name for polymorphic deserialization
    """

    model_config = standard_model_config()

    schema_discriminator: ClassVar[str] = "kind"

    @classmethod
    def __pydantic_schema_base_type__(cls) -> type[ProfileArgs]:
        """
        Return base type for polymorphic validation hierarchy.

        :return: Base ProfileArgs class for schema validation
        """
        if cls.__name__ == "ProfileArgs":
            return cls

        return ProfileArgs

    kind: str = Field(
        description="Profile type discriminator",
        examples=["concurrent", "synchronous"],
    )
    rampup_duration: NonNegativeFloat = Field(
        default=0.0,
        description=("Duration in seconds to ramp up the targeted scheduling rate"),
    )
    warmup: TransientPhaseConfig = Field(
        default_factory=TransientPhaseConfig,
        description="Warmup phase to exclude initial transient period",
        examples=[0.0, 1.0, {"mode": "percent", "percent": 2.0}],
    )
    cooldown: TransientPhaseConfig = Field(
        default_factory=TransientPhaseConfig,
        description="Cooldown phase to exclude final transient period",
        examples=[0.0, 1.0, {"mode": "duration", "value": 2.0}],
    )

    @field_validator("warmup", "cooldown", mode="before")
    @classmethod
    def _coerce_transient_phase(cls, v: Any) -> Any:
        if isinstance(v, str):
            with contextlib.suppress(json.JSONDecodeError, ValueError):
                v = json.loads(v)
        if isinstance(v, int | float | None):
            return TransientPhaseConfig.create_from_value(v)
        return v

__pydantic_schema_base_type__() classmethod

Return base type for polymorphic validation hierarchy.

Returns:

Type Description
type[ProfileArgs]

Base ProfileArgs class for schema validation

Source code in src/guidellm/benchmark/schemas/profiles.py
@classmethod
def __pydantic_schema_base_type__(cls) -> type[ProfileArgs]:
    """
    Return base type for polymorphic validation hierarchy.

    :return: Base ProfileArgs class for schema validation
    """
    if cls.__name__ == "ProfileArgs":
        return cls

    return ProfileArgs

RandomArgs

Bases: PydanticClassRegistryMixin['RandomArgs'], ABC

Base class for random initialization arguments.

Attributes:

Name Type Description
schema_discriminator str

Field name for polymorphic deserialization

Source code in src/guidellm/benchmark/schemas/random.py
class RandomArgs(PydanticClassRegistryMixin["RandomArgs"], ABC):
    """Base class for random initialization arguments.

    :cvar schema_discriminator: Field name for polymorphic deserialization
    """

    model_config = standard_model_config()

    schema_discriminator: ClassVar[str] = "kind"

    @classmethod
    def __pydantic_schema_base_type__(cls) -> type[RandomArgs]:
        """
        Return base type for polymorphic validation hierarchy.

        :return: Base RandomArgs class for schema validation
        """
        if cls.__name__ == "RandomArgs":
            return cls

        return RandomArgs

    kind: str = Field(
        description="The kind of random configuration to use.",
    )

__pydantic_schema_base_type__() classmethod

Return base type for polymorphic validation hierarchy.

Returns:

Type Description
type[RandomArgs]

Base RandomArgs class for schema validation

Source code in src/guidellm/benchmark/schemas/random.py
@classmethod
def __pydantic_schema_base_type__(cls) -> type[RandomArgs]:
    """
    Return base type for polymorphic validation hierarchy.

    :return: Base RandomArgs class for schema validation
    """
    if cls.__name__ == "RandomArgs":
        return cls

    return RandomArgs

RunningMetricStats

Bases: StandardBaseModel

Maintains running statistics for a metric stream without storing all samples.

Accumulates count, sum, time-weighted sum, and duration to compute mean, rate, and time-weighted statistics incrementally. Efficient for real-time metric tracking during long-running benchmarks where storing individual samples is impractical.

Source code in src/guidellm/benchmark/schemas/accumulator.py
class RunningMetricStats(StandardBaseModel):
    """
    Maintains running statistics for a metric stream without storing all samples.

    Accumulates count, sum, time-weighted sum, and duration to compute mean, rate,
    and time-weighted statistics incrementally. Efficient for real-time metric tracking
    during long-running benchmarks where storing individual samples is impractical.
    """

    count: int = Field(description="Number of samples accumulated", default=0)
    value_sum: float = Field(description="Total sum of accumulated values", default=0.0)
    time_weighted_sum: float = Field(
        description="Time-weighted sum of accumulated values", default=0.0
    )
    duration: float = Field(
        description="Total duration over which values were accumulated", default=0.0
    )
    last_value: float | None = Field(
        description="Most recent value added to the accumulator", default=None
    )

    @property
    def mean(self) -> float | None:
        """
        :return: Arithmetic mean of accumulated values, or None if no samples
        """
        if self.count <= 0:
            return None

        return self.value_sum / self.count

    @property
    def time_weighted_mean(self) -> float | None:
        """
        :return: Time-weighted mean considering duration between samples, or None
        """
        if self.duration <= 0.0:
            return None

        return self.time_weighted_sum / self.duration

    @property
    def rate_per_item(self) -> float | None:
        """
        :return: Average value per accumulated item, or None if no samples
        """
        if self.count <= 0:
            return None

        return self.value_sum / self.count

    @property
    def rate_per_second(self) -> float | None:
        """
        :return: Average value per second of duration, or None if no duration
        """
        if self.duration <= 0.0:
            return None

        return self.value_sum / self.duration

    def update_estimate(
        self,
        value: float | None,
        count: int = 1,
        duration: float | None = None,
        elapsed: float | None = None,
    ):
        """
        Incorporate a new metric value into running statistics.

        Updates count, sum, and time-weighted statistics using the new value and timing
        information. Time-weighted calculations use the previous value over the elapsed
        interval to capture sustained metric behavior.

        :param value: New metric value to accumulate
        :param count: Number of occurrences this value represents
        :param duration: Total duration to set, overriding incremental elapsed updates
        :param elapsed: Time elapsed since last update for time-weighted calculations
        """
        if value is not None:
            self.count += count
            self.value_sum += value * count

        if elapsed is not None:
            self.time_weighted_sum += (self.last_value or 0.0) * elapsed

        self.duration = (
            duration if duration is not None else (self.duration + (elapsed or 0.0))
        )
        self.last_value = value

mean property

Returns:

Type Description
float | None

Arithmetic mean of accumulated values, or None if no samples

rate_per_item property

Returns:

Type Description
float | None

Average value per accumulated item, or None if no samples

rate_per_second property

Returns:

Type Description
float | None

Average value per second of duration, or None if no duration

time_weighted_mean property

Returns:

Type Description
float | None

Time-weighted mean considering duration between samples, or None

update_estimate(value, count=1, duration=None, elapsed=None)

Incorporate a new metric value into running statistics.

Updates count, sum, and time-weighted statistics using the new value and timing information. Time-weighted calculations use the previous value over the elapsed interval to capture sustained metric behavior.

Parameters:

Name Type Description Default
value float | None

New metric value to accumulate

required
count int

Number of occurrences this value represents

1
duration float | None

Total duration to set, overriding incremental elapsed updates

None
elapsed float | None

Time elapsed since last update for time-weighted calculations

None
Source code in src/guidellm/benchmark/schemas/accumulator.py
def update_estimate(
    self,
    value: float | None,
    count: int = 1,
    duration: float | None = None,
    elapsed: float | None = None,
):
    """
    Incorporate a new metric value into running statistics.

    Updates count, sum, and time-weighted statistics using the new value and timing
    information. Time-weighted calculations use the previous value over the elapsed
    interval to capture sustained metric behavior.

    :param value: New metric value to accumulate
    :param count: Number of occurrences this value represents
    :param duration: Total duration to set, overriding incremental elapsed updates
    :param elapsed: Time elapsed since last update for time-weighted calculations
    """
    if value is not None:
        self.count += count
        self.value_sum += value * count

    if elapsed is not None:
        self.time_weighted_sum += (self.last_value or 0.0) * elapsed

    self.duration = (
        duration if duration is not None else (self.duration + (elapsed or 0.0))
    )
    self.last_value = value

SchedulerMetrics

Bases: StandardBaseDict

Scheduler timing and performance statistics.

Tracks overall benchmark timing, request counts by status, and detailed internal scheduler performance metrics including queue times, processing delays, and request execution statistics. Used to analyze scheduler efficiency and identify bottlenecks in request processing pipelines.

Source code in src/guidellm/benchmark/schemas/metrics.py
class SchedulerMetrics(StandardBaseDict):
    """Scheduler timing and performance statistics.

    Tracks overall benchmark timing, request counts by status, and detailed internal
    scheduler performance metrics including queue times, processing delays, and
    request execution statistics. Used to analyze scheduler efficiency and identify
    bottlenecks in request processing pipelines.
    """

    # Overall timings for the scheduler
    start_time: float = Field(
        description="Unix timestamp when the benchmark run started"
    )
    request_start_time: float = Field(
        description="Unix timestamp when first request was made"
    )
    measure_start_time: float = Field(
        description="Unix timestamp when measurement period started"
    )
    measure_end_time: float = Field(
        description="Unix timestamp when measurement period ended"
    )
    request_end_time: float = Field(
        description="Unix timestamp when last request completed"
    )
    end_time: float = Field(description="Unix timestamp when the benchmark run ended")

    # Request details tracked by the scheduler
    requests_made: StatusBreakdown[int, int, int, int] = Field(
        description="Request counts by status: successful, incomplete, errored, total"
    )

    # Scheduler internal performance timings
    queued_time_avg: float = Field(
        description="Avg time requests spent in the queue (seconds)"
    )
    resolve_start_delay_avg: float = Field(
        description="Avg delay before worker begins resolving req after dequeue (sec)"
    )
    resolve_targeted_start_delay_avg: float = Field(
        description="Avg delay to targeted resolve start time (seconds)"
    )
    request_start_delay_avg: float = Field(
        description="Avg delay before request starts after resolve (seconds)"
    )
    request_targeted_start_delay_avg: float = Field(
        description="Avg delay to targeted request start time (seconds)"
    )
    request_time_avg: float = Field(description="Avg request execution time (seconds)")
    resolve_end_delay_avg: float = Field(
        description="Avg delay after request completes before resolve ends (seconds)"
    )
    resolve_time_avg: float = Field(
        description="Avg total resolve time including request (seconds)"
    )
    finalized_delay_avg: float = Field(
        description="Avg delay from resolve end to request finalization (seconds)"
    )
    processed_delay_avg: float = Field(
        description="Avg delay from finalization to processing completion (seconds)"
    )

    @classmethod
    def compile(
        cls,
        accumulator: GenerativeBenchmarkAccumulator,
        scheduler_state: SchedulerState,
    ) -> SchedulerMetrics:
        """
        Compile scheduler metrics from accumulator and scheduler state.

        :param accumulator: Benchmark accumulator containing timing and metric data
        :param scheduler_state: Scheduler state with execution timing information
        :return: Compiled scheduler metrics with performance statistics
        """
        return SchedulerMetrics(
            # Overall timings for the scheduler
            start_time=scheduler_state.start_time,
            request_start_time=accumulator.timings.finalized_request_start,
            measure_start_time=accumulator.timings.finalized_measure_start,
            measure_end_time=accumulator.timings.finalized_measure_end,
            request_end_time=accumulator.timings.finalized_request_end,
            end_time=scheduler_state.end_time or -1.0,
            # Request details tracked by the scheduler
            requests_made=accumulator.scheduler_metrics.requests_made,
            # Scheduler internal performance timings
            queued_time_avg=accumulator.scheduler_metrics.queued_time.mean or -1.0,
            resolve_start_delay_avg=(
                accumulator.scheduler_metrics.resolve_start_delay.mean or -1.0
            ),
            resolve_targeted_start_delay_avg=(
                accumulator.scheduler_metrics.resolve_targeted_start_delay.mean or -1.0
            ),
            request_start_delay_avg=(
                accumulator.scheduler_metrics.request_start_delay.mean or -1.0
            ),
            request_targeted_start_delay_avg=(
                accumulator.scheduler_metrics.request_targeted_start_delay.mean or -1.0
            ),
            request_time_avg=accumulator.scheduler_metrics.request_time.mean or -1.0,
            resolve_end_delay_avg=(
                accumulator.scheduler_metrics.resolve_end_delay.mean or -1.0
            ),
            resolve_time_avg=accumulator.scheduler_metrics.resolve_time.mean or -1.0,
            finalized_delay_avg=(
                accumulator.scheduler_metrics.finalized_delay.mean or -1.0
            ),
            processed_delay_avg=(
                accumulator.scheduler_metrics.processed_delay.mean or -1.0
            ),
        )

compile(accumulator, scheduler_state) classmethod

Compile scheduler metrics from accumulator and scheduler state.

Parameters:

Name Type Description Default
accumulator GenerativeBenchmarkAccumulator

Benchmark accumulator containing timing and metric data

required
scheduler_state SchedulerState

Scheduler state with execution timing information

required

Returns:

Type Description
SchedulerMetrics

Compiled scheduler metrics with performance statistics

Source code in src/guidellm/benchmark/schemas/metrics.py
@classmethod
def compile(
    cls,
    accumulator: GenerativeBenchmarkAccumulator,
    scheduler_state: SchedulerState,
) -> SchedulerMetrics:
    """
    Compile scheduler metrics from accumulator and scheduler state.

    :param accumulator: Benchmark accumulator containing timing and metric data
    :param scheduler_state: Scheduler state with execution timing information
    :return: Compiled scheduler metrics with performance statistics
    """
    return SchedulerMetrics(
        # Overall timings for the scheduler
        start_time=scheduler_state.start_time,
        request_start_time=accumulator.timings.finalized_request_start,
        measure_start_time=accumulator.timings.finalized_measure_start,
        measure_end_time=accumulator.timings.finalized_measure_end,
        request_end_time=accumulator.timings.finalized_request_end,
        end_time=scheduler_state.end_time or -1.0,
        # Request details tracked by the scheduler
        requests_made=accumulator.scheduler_metrics.requests_made,
        # Scheduler internal performance timings
        queued_time_avg=accumulator.scheduler_metrics.queued_time.mean or -1.0,
        resolve_start_delay_avg=(
            accumulator.scheduler_metrics.resolve_start_delay.mean or -1.0
        ),
        resolve_targeted_start_delay_avg=(
            accumulator.scheduler_metrics.resolve_targeted_start_delay.mean or -1.0
        ),
        request_start_delay_avg=(
            accumulator.scheduler_metrics.request_start_delay.mean or -1.0
        ),
        request_targeted_start_delay_avg=(
            accumulator.scheduler_metrics.request_targeted_start_delay.mean or -1.0
        ),
        request_time_avg=accumulator.scheduler_metrics.request_time.mean or -1.0,
        resolve_end_delay_avg=(
            accumulator.scheduler_metrics.resolve_end_delay.mean or -1.0
        ),
        resolve_time_avg=accumulator.scheduler_metrics.resolve_time.mean or -1.0,
        finalized_delay_avg=(
            accumulator.scheduler_metrics.finalized_delay.mean or -1.0
        ),
        processed_delay_avg=(
            accumulator.scheduler_metrics.processed_delay.mean or -1.0
        ),
    )

SchedulerMetricsAccumulator

Bases: StandardBaseModel

Tracks scheduler-level timing and overhead metrics during execution.

Monitors request lifecycle timing from queuing through completion, capturing delays at each stage: queue time, worker start delays, request processing time, and finalization overhead. Provides insight into scheduler efficiency and bottleneck identification in request orchestration.

Source code in src/guidellm/benchmark/schemas/accumulator.py
class SchedulerMetricsAccumulator(StandardBaseModel):
    """
    Tracks scheduler-level timing and overhead metrics during execution.

    Monitors request lifecycle timing from queuing through completion, capturing delays
    at each stage: queue time, worker start delays, request processing time, and
    finalization overhead. Provides insight into scheduler efficiency and bottleneck
    identification in request orchestration.
    """

    requests_made: StatusBreakdown[int, int, int, int] = Field(
        description="Request counts by status: successful, incomplete, errored, total",
        default_factory=lambda: StatusBreakdown[int, int, int, int](
            successful=0, errored=0, incomplete=0, total=0
        ),
    )
    # Timings flow:
    # Request scheduling: queued->dequeued->scheduled_at->resolve_start->
    # Request processing: request_start->*_iteration->request_end->
    # Request finalizing: resolve_end->finalized->accumulation update processed
    queued_time: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Running stats for time requests spent in the queue",
    )
    resolve_start_delay: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description=(
            "Running stats for delay before worker begins resolving req after dequeue"
        ),
    )
    resolve_targeted_start_delay: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description=(
            "Running stats for delay from targeted start to actual worker start"
        ),
    )
    request_start_delay: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Running stats for delay after resolve til request start",
    )
    request_targeted_start_delay: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description=(
            "Running stats for delay from targeted start to actual request start"
        ),
    )
    request_time: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Running stats for request processing time",
    )
    resolve_end_delay: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Running stats for delay after request end till worker resolves",
    )
    resolve_time: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Running stats for time for worker to resolve requests",
    )
    finalized_delay: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description="Running stats for delay after resolve til finalized in scheduler",
    )
    processed_delay: RunningMetricStats = Field(
        default_factory=RunningMetricStats,
        description=(
            "Running stats for delay from finalized til request being "
            "processed by accumulation"
        ),
    )

    def update_estimate(
        self, scheduler_state: SchedulerState, stats: GenerativeRequestStats
    ):
        """
        Update scheduler metrics with completed request timing data.

        Extracts timing information from request statistics to update running metrics
        for each scheduler lifecycle stage. Validates that required timing markers are
        present before processing.

        :param scheduler_state: Current scheduler state with request counts
        :param stats: Completed request statistics with detailed timing information
        :raises ValueError: If required timing markers are missing
        """
        # Update request counts
        self.requests_made.successful = scheduler_state.successful_requests
        self.requests_made.errored = scheduler_state.errored_requests
        self.requests_made.incomplete = scheduler_state.cancelled_requests
        self.requests_made.total = (
            scheduler_state.successful_requests
            + scheduler_state.errored_requests
            + scheduler_state.cancelled_requests
        )

        # All requests must have queued, dequeued, resolve_end, and finalized timings
        timings: RequestTimings = stats.info.timings
        if any(
            timing is None
            for timing in [
                timings.queued,
                timings.dequeued,
                timings.resolve_end,
                timings.finalized,
            ]
        ):
            raise ValueError(
                "Required timings 'queued', 'dequeued', 'resolve_end', and "
                "'finalized' must not be None"
            )

        # Store validated non-None timings for type safety
        queued: float = timings.queued  # type: ignore[assignment]
        dequeued: float = timings.dequeued  # type: ignore[assignment]
        resolve_end: float = timings.resolve_end  # type: ignore[assignment]
        finalized: float = timings.finalized  # type: ignore[assignment]

        # Update timing metrics in occurrence order
        self.queued_time.update_estimate(value=dequeued - queued)

        if timings.scheduled_at is not None and timings.resolve_start is not None:
            self.resolve_start_delay.update_estimate(
                value=timings.resolve_start - timings.scheduled_at
            )

        if timings.targeted_start is not None and timings.resolve_start is not None:
            self.resolve_targeted_start_delay.update_estimate(
                value=timings.resolve_start - timings.targeted_start
            )

        if timings.resolve_start is not None and timings.request_start is not None:
            self.request_start_delay.update_estimate(
                value=timings.request_start - timings.resolve_start
            )

        if timings.targeted_start is not None and timings.request_start is not None:
            self.request_targeted_start_delay.update_estimate(
                value=timings.request_start - timings.targeted_start
            )

        if timings.request_start is not None and timings.request_end is not None:
            self.request_time.update_estimate(
                value=timings.request_end - timings.request_start
            )

        if timings.request_end is not None:
            self.resolve_end_delay.update_estimate(
                value=resolve_end - timings.request_end
            )

        if timings.resolve_start is not None:
            self.resolve_time.update_estimate(value=resolve_end - timings.resolve_start)

        self.finalized_delay.update_estimate(value=finalized - resolve_end)
        self.processed_delay.update_estimate(value=time.time() - finalized)

update_estimate(scheduler_state, stats)

Update scheduler metrics with completed request timing data.

Extracts timing information from request statistics to update running metrics for each scheduler lifecycle stage. Validates that required timing markers are present before processing.

Parameters:

Name Type Description Default
scheduler_state SchedulerState

Current scheduler state with request counts

required
stats GenerativeRequestStats

Completed request statistics with detailed timing information

required

Raises:

Type Description
ValueError

If required timing markers are missing

Source code in src/guidellm/benchmark/schemas/accumulator.py
def update_estimate(
    self, scheduler_state: SchedulerState, stats: GenerativeRequestStats
):
    """
    Update scheduler metrics with completed request timing data.

    Extracts timing information from request statistics to update running metrics
    for each scheduler lifecycle stage. Validates that required timing markers are
    present before processing.

    :param scheduler_state: Current scheduler state with request counts
    :param stats: Completed request statistics with detailed timing information
    :raises ValueError: If required timing markers are missing
    """
    # Update request counts
    self.requests_made.successful = scheduler_state.successful_requests
    self.requests_made.errored = scheduler_state.errored_requests
    self.requests_made.incomplete = scheduler_state.cancelled_requests
    self.requests_made.total = (
        scheduler_state.successful_requests
        + scheduler_state.errored_requests
        + scheduler_state.cancelled_requests
    )

    # All requests must have queued, dequeued, resolve_end, and finalized timings
    timings: RequestTimings = stats.info.timings
    if any(
        timing is None
        for timing in [
            timings.queued,
            timings.dequeued,
            timings.resolve_end,
            timings.finalized,
        ]
    ):
        raise ValueError(
            "Required timings 'queued', 'dequeued', 'resolve_end', and "
            "'finalized' must not be None"
        )

    # Store validated non-None timings for type safety
    queued: float = timings.queued  # type: ignore[assignment]
    dequeued: float = timings.dequeued  # type: ignore[assignment]
    resolve_end: float = timings.resolve_end  # type: ignore[assignment]
    finalized: float = timings.finalized  # type: ignore[assignment]

    # Update timing metrics in occurrence order
    self.queued_time.update_estimate(value=dequeued - queued)

    if timings.scheduled_at is not None and timings.resolve_start is not None:
        self.resolve_start_delay.update_estimate(
            value=timings.resolve_start - timings.scheduled_at
        )

    if timings.targeted_start is not None and timings.resolve_start is not None:
        self.resolve_targeted_start_delay.update_estimate(
            value=timings.resolve_start - timings.targeted_start
        )

    if timings.resolve_start is not None and timings.request_start is not None:
        self.request_start_delay.update_estimate(
            value=timings.request_start - timings.resolve_start
        )

    if timings.targeted_start is not None and timings.request_start is not None:
        self.request_targeted_start_delay.update_estimate(
            value=timings.request_start - timings.targeted_start
        )

    if timings.request_start is not None and timings.request_end is not None:
        self.request_time.update_estimate(
            value=timings.request_end - timings.request_start
        )

    if timings.request_end is not None:
        self.resolve_end_delay.update_estimate(
            value=resolve_end - timings.request_end
        )

    if timings.resolve_start is not None:
        self.resolve_time.update_estimate(value=resolve_end - timings.resolve_start)

    self.finalized_delay.update_estimate(value=finalized - resolve_end)
    self.processed_delay.update_estimate(value=time.time() - finalized)

TransientPhaseConfig

Bases: StandardBaseModel

Configure warmup and cooldown phases for benchmark execution.

Supports flexible phase definition through percentage or absolute value specifications with multiple interpretation modes. Phases can be bounded by duration, request count, or both, enabling precise control over transient periods that should be excluded from final benchmark metrics.

Source code in src/guidellm/benchmark/schemas/base.py
class TransientPhaseConfig(StandardBaseModel):
    """Configure warmup and cooldown phases for benchmark execution.

    Supports flexible phase definition through percentage or absolute value
    specifications with multiple interpretation modes. Phases can be bounded
    by duration, request count, or both, enabling precise control over transient
    periods that should be excluded from final benchmark metrics.
    """

    @classmethod
    def create_from_value(
        cls, value: int | float | dict | TransientPhaseConfig | None
    ) -> TransientPhaseConfig:
        """
        Create configuration from flexible input formats.

        :param value: Configuration as int/float (percent if <1.0, absolute
            otherwise), dict (validated to model), TransientPhaseConfig instance,
            or None for defaults
        :return: Configured TransientPhaseConfig instance
        :raises ValueError: If value type is unsupported
        """
        if value is None:
            return TransientPhaseConfig()

        if isinstance(value, TransientPhaseConfig):
            return value

        if isinstance(value, dict):
            return TransientPhaseConfig.model_validate(value)

        if isinstance(value, int | float):
            kwargs = {
                "percent": value if value < 1.0 else None,
                "value": value if value >= 1.0 else None,
            }
            return TransientPhaseConfig.model_validate(kwargs)

        raise ValueError(f"Unsupported type for TransientPhaseConfig: {type(value)}")

    percent: NonNegativeFloat | None = Field(
        default=None,
        description=(
            "Phase size as percentage (0.0-1.0) of total duration/requests; "
            "interpretation depends on mode. Takes precedence over value when target "
            "mode is available, otherwise falls back to value"
        ),
        examples=[0.0, 0.5],
        lt=1.0,
    )
    value: NonNegativeInt | NonNegativeFloat | None = Field(
        default=None,
        description=(
            "Phase size as absolute duration (seconds) or request count; "
            "interpretation depends on mode. Used when percent is unset or "
            "target mode unavailable"
        ),
        examples=[1.0, 2.0],
    )
    mode: Literal[
        "duration", "requests", "prefer_duration", "prefer_requests", "both"
    ] = Field(
        default="prefer_duration",
        description=(
            "Interpretation mode: 'duration' for time-based phases, 'requests' for "
            "count-based phases, 'prefer_duration'/'prefer_requests' for fallback "
            "behavior, 'both' requires satisfying both conditions"
        ),
    )

    def compute_limits(
        self,
        max_requests: int | float | None,
        max_seconds: float | None,
        enforce_preference: bool = True,
    ) -> tuple[float | None, int | None]:
        """
        Calculate phase boundaries from benchmark constraints.

        :param max_requests: Total request budget for benchmark execution
        :param max_seconds: Total duration budget for benchmark execution
        :param enforce_preference: Whether to enforce preferred mode when both
            duration and request constraints are available
        :return: Tuple of (phase duration in seconds, phase request count)
        """
        duration: float | None = None
        requests: int | None = None

        if self.mode != "requests" and max_seconds is not None:
            if self.percent is not None:
                duration = self.percent * max_seconds
            elif self.value is not None:
                duration = float(self.value)

        if self.mode != "duration" and max_requests is not None:
            if self.percent is not None:
                requests = int(self.percent * max_requests)
            elif self.value is not None:
                requests = int(self.value)

        if enforce_preference:
            if self.mode == "prefer_duration" and duration is not None:
                requests = None
            elif self.mode == "prefer_requests" and requests is not None:
                duration = None

        return duration, requests

    def compute_transition_time(
        self,
        info: RequestInfo,
        state: SchedulerState,
        period: Literal["start", "end"],
    ) -> tuple[bool, float | None]:
        """
        Determine transition timestamp for entering or exiting phase.

        :param info: RequestInfo for current request to calculate against
        :param state: SchedulerState with current progress metrics and scheduler info
        :param period: Phase period, either "start" for warmup or "end" for cooldown
        :return: Tuple of (phase active flag, transition timestamp if applicable)
        """
        phase_duration, phase_requests = self.compute_limits(
            max_requests=state.progress.total_requests,
            max_seconds=state.progress.total_duration,
        )
        duration_transition_time: float | None = None
        request_transition_time: float | None = None

        # Calculate transition times for the phase based on phase limits and period
        # Potential phases: start (warmup) -> active -> end (cooldown)
        #   Warmup transition times: (start, start + duration)
        #   Active transition times: (start + duration, end - duration)
        #   Cooldown transition times: (end - duration, end)
        if period == "start":
            if phase_duration is not None:
                # Duration was set and caculating for "warmup" / start phase
                # Phase is active for [start, start + duration]
                duration_transition_time = state.start_time + phase_duration
            if phase_requests is not None:
                # Requests was set and calculating for "warmup" / start phase
                # Phase is active for requests [0, phase_requests]
                # Grab start time of the next request as transition time
                # (all requests up to and including phase_requests are in warmup)
                request_transition_time = (
                    info.started_at
                    if info.started_at is not None
                    and state.processed_requests == phase_requests + 1
                    else -1.0
                )
        elif period == "end":
            if phase_duration is not None:
                # Duration was set and calculating for "cooldown" / end phase
                # Phase is active for [end - duration, end]
                duration_transition_time = (
                    state.start_time + state.progress.total_duration - phase_duration
                    if state.progress.total_duration is not None
                    else -1.0
                )
            if phase_requests is not None:
                # Requests was set and calculating for "cooldown" / end phase
                # Phase is active for requests [total - phase_requests, total]
                # Grab completion time of the request right before cooldown starts
                # (all requests from that point onward are in cooldown)
                request_transition_time = (
                    info.completed_at
                    if info.completed_at is not None
                    and state.progress.remaining_requests is not None
                    and state.progress.remaining_requests == phase_requests + 1
                    else -1.0
                )

        transition_active: bool = False
        transition_time: float | None = None

        if request_transition_time == -1.0 or duration_transition_time == -1.0:
            # Transition defined but not yet reached or passed
            transition_active = True
            request_transition_time = None
        elif (
            request_transition_time is not None and duration_transition_time is not None
        ):
            # Both limits defined; need to satisfy both (min for end, max for start)
            transition_active = True
            transition_time = (
                min(request_transition_time, duration_transition_time)
                if period == "end"
                else max(request_transition_time, duration_transition_time)
            )
        elif (
            request_transition_time is not None or duration_transition_time is not None
        ):
            # One limit defined; satisfy that one
            transition_active = True
            transition_time = request_transition_time or duration_transition_time

        return transition_active, transition_time

compute_limits(max_requests, max_seconds, enforce_preference=True)

Calculate phase boundaries from benchmark constraints.

Parameters:

Name Type Description Default
max_requests int | float | None

Total request budget for benchmark execution

required
max_seconds float | None

Total duration budget for benchmark execution

required
enforce_preference bool

Whether to enforce preferred mode when both duration and request constraints are available

True

Returns:

Type Description
tuple[float | None, int | None]

Tuple of (phase duration in seconds, phase request count)

Source code in src/guidellm/benchmark/schemas/base.py
def compute_limits(
    self,
    max_requests: int | float | None,
    max_seconds: float | None,
    enforce_preference: bool = True,
) -> tuple[float | None, int | None]:
    """
    Calculate phase boundaries from benchmark constraints.

    :param max_requests: Total request budget for benchmark execution
    :param max_seconds: Total duration budget for benchmark execution
    :param enforce_preference: Whether to enforce preferred mode when both
        duration and request constraints are available
    :return: Tuple of (phase duration in seconds, phase request count)
    """
    duration: float | None = None
    requests: int | None = None

    if self.mode != "requests" and max_seconds is not None:
        if self.percent is not None:
            duration = self.percent * max_seconds
        elif self.value is not None:
            duration = float(self.value)

    if self.mode != "duration" and max_requests is not None:
        if self.percent is not None:
            requests = int(self.percent * max_requests)
        elif self.value is not None:
            requests = int(self.value)

    if enforce_preference:
        if self.mode == "prefer_duration" and duration is not None:
            requests = None
        elif self.mode == "prefer_requests" and requests is not None:
            duration = None

    return duration, requests

compute_transition_time(info, state, period)

Determine transition timestamp for entering or exiting phase.

Parameters:

Name Type Description Default
info RequestInfo

RequestInfo for current request to calculate against

required
state SchedulerState

SchedulerState with current progress metrics and scheduler info

required
period Literal['start', 'end']

Phase period, either "start" for warmup or "end" for cooldown

required

Returns:

Type Description
tuple[bool, float | None]

Tuple of (phase active flag, transition timestamp if applicable)

Source code in src/guidellm/benchmark/schemas/base.py
def compute_transition_time(
    self,
    info: RequestInfo,
    state: SchedulerState,
    period: Literal["start", "end"],
) -> tuple[bool, float | None]:
    """
    Determine transition timestamp for entering or exiting phase.

    :param info: RequestInfo for current request to calculate against
    :param state: SchedulerState with current progress metrics and scheduler info
    :param period: Phase period, either "start" for warmup or "end" for cooldown
    :return: Tuple of (phase active flag, transition timestamp if applicable)
    """
    phase_duration, phase_requests = self.compute_limits(
        max_requests=state.progress.total_requests,
        max_seconds=state.progress.total_duration,
    )
    duration_transition_time: float | None = None
    request_transition_time: float | None = None

    # Calculate transition times for the phase based on phase limits and period
    # Potential phases: start (warmup) -> active -> end (cooldown)
    #   Warmup transition times: (start, start + duration)
    #   Active transition times: (start + duration, end - duration)
    #   Cooldown transition times: (end - duration, end)
    if period == "start":
        if phase_duration is not None:
            # Duration was set and caculating for "warmup" / start phase
            # Phase is active for [start, start + duration]
            duration_transition_time = state.start_time + phase_duration
        if phase_requests is not None:
            # Requests was set and calculating for "warmup" / start phase
            # Phase is active for requests [0, phase_requests]
            # Grab start time of the next request as transition time
            # (all requests up to and including phase_requests are in warmup)
            request_transition_time = (
                info.started_at
                if info.started_at is not None
                and state.processed_requests == phase_requests + 1
                else -1.0
            )
    elif period == "end":
        if phase_duration is not None:
            # Duration was set and calculating for "cooldown" / end phase
            # Phase is active for [end - duration, end]
            duration_transition_time = (
                state.start_time + state.progress.total_duration - phase_duration
                if state.progress.total_duration is not None
                else -1.0
            )
        if phase_requests is not None:
            # Requests was set and calculating for "cooldown" / end phase
            # Phase is active for requests [total - phase_requests, total]
            # Grab completion time of the request right before cooldown starts
            # (all requests from that point onward are in cooldown)
            request_transition_time = (
                info.completed_at
                if info.completed_at is not None
                and state.progress.remaining_requests is not None
                and state.progress.remaining_requests == phase_requests + 1
                else -1.0
            )

    transition_active: bool = False
    transition_time: float | None = None

    if request_transition_time == -1.0 or duration_transition_time == -1.0:
        # Transition defined but not yet reached or passed
        transition_active = True
        request_transition_time = None
    elif (
        request_transition_time is not None and duration_transition_time is not None
    ):
        # Both limits defined; need to satisfy both (min for end, max for start)
        transition_active = True
        transition_time = (
            min(request_transition_time, duration_transition_time)
            if period == "end"
            else max(request_transition_time, duration_transition_time)
        )
    elif (
        request_transition_time is not None or duration_transition_time is not None
    ):
        # One limit defined; satisfy that one
        transition_active = True
        transition_time = request_transition_time or duration_transition_time

    return transition_active, transition_time

create_from_value(value) classmethod

Create configuration from flexible input formats.

Parameters:

Name Type Description Default
value int | float | dict | TransientPhaseConfig | None

Configuration as int/float (percent if <1.0, absolute otherwise), dict (validated to model), TransientPhaseConfig instance, or None for defaults

required

Returns:

Type Description
TransientPhaseConfig

Configured TransientPhaseConfig instance

Raises:

Type Description
ValueError

If value type is unsupported

Source code in src/guidellm/benchmark/schemas/base.py
@classmethod
def create_from_value(
    cls, value: int | float | dict | TransientPhaseConfig | None
) -> TransientPhaseConfig:
    """
    Create configuration from flexible input formats.

    :param value: Configuration as int/float (percent if <1.0, absolute
        otherwise), dict (validated to model), TransientPhaseConfig instance,
        or None for defaults
    :return: Configured TransientPhaseConfig instance
    :raises ValueError: If value type is unsupported
    """
    if value is None:
        return TransientPhaseConfig()

    if isinstance(value, TransientPhaseConfig):
        return value

    if isinstance(value, dict):
        return TransientPhaseConfig.model_validate(value)

    if isinstance(value, int | float):
        kwargs = {
            "percent": value if value < 1.0 else None,
            "value": value if value >= 1.0 else None,
        }
        return TransientPhaseConfig.model_validate(kwargs)

    raise ValueError(f"Unsupported type for TransientPhaseConfig: {type(value)}")