|
11 | 11 | from __future__ import annotations |
12 | 12 |
|
13 | 13 | import json |
| 14 | +from abc import ABC |
14 | 15 | from pathlib import Path |
15 | | -from typing import Any |
| 16 | +from typing import Any, ClassVar, Literal |
16 | 17 |
|
17 | 18 | import yaml |
18 | 19 | from pydantic import ( |
|
39 | 40 | ) |
40 | 41 | from guidellm.scheduler.constraints import ConstraintArgs |
41 | 42 | from guidellm.schemas import ( |
| 43 | + PydanticClassRegistryMixin, |
42 | 44 | ReloadableBaseModel, |
43 | 45 | StandardBaseModel, |
44 | 46 | standard_model_config, |
|
49 | 51 | "BenchmarkArgs", |
50 | 52 | "BenchmarkMetadata", |
51 | 53 | "BenchmarkScenario", |
| 54 | + "GenerativeMetricsArgs", |
| 55 | + "MetricsArgs", |
52 | 56 | ] |
53 | 57 |
|
54 | 58 |
|
@@ -77,6 +81,61 @@ def default_kind_list(*kinds: str) -> list[dict[str, Any]]: |
77 | 81 | return [default_kind(kind) for kind in kinds] |
78 | 82 |
|
79 | 83 |
|
| 84 | +class MetricsArgs(PydanticClassRegistryMixin["MetricsArgs"], ABC): |
| 85 | + """Base class for metrics collection arguments. |
| 86 | +
|
| 87 | + :cvar schema_discriminator: Field name for polymorphic deserialization |
| 88 | + """ |
| 89 | + |
| 90 | + model_config = standard_model_config() |
| 91 | + |
| 92 | + schema_discriminator: ClassVar[str] = "kind" |
| 93 | + |
| 94 | + @classmethod |
| 95 | + def __pydantic_schema_base_type__(cls) -> type[MetricsArgs]: |
| 96 | + """ |
| 97 | + Return base type for polymorphic validation hierarchy. |
| 98 | +
|
| 99 | + :return: Base MetricsArgs class for schema validation |
| 100 | + """ |
| 101 | + if cls.__name__ == "MetricsArgs": |
| 102 | + return cls |
| 103 | + |
| 104 | + return MetricsArgs |
| 105 | + |
| 106 | + kind: str = Field( |
| 107 | + description="The kind of metrics configuration to use.", |
| 108 | + ) |
| 109 | + |
| 110 | + |
| 111 | +@MetricsArgs.register("generative") |
| 112 | +class GenerativeMetricsArgs(MetricsArgs): |
| 113 | + """Metrics configuration for generative (autoregressive) benchmarks.""" |
| 114 | + |
| 115 | + kind: Literal["generative"] = Field( |
| 116 | + default="generative", |
| 117 | + description="The kind of metrics configuration to use.", |
| 118 | + ) |
| 119 | + sample_size: int | None = Field( |
| 120 | + default=None, |
| 121 | + description=( |
| 122 | + "Maximum number of requests per status group (completed, errored, " |
| 123 | + "incomplete) to retain full data (prompt, output, tool calls) for in " |
| 124 | + "the final benchmark. Lightweight stats (latency, token counts) are " |
| 125 | + "always kept for every request. None keeps all request data, 0 strips " |
| 126 | + "all request data, N > 0 uses reservoir sampling to retain N per group." |
| 127 | + ), |
| 128 | + examples=[None, 0, 100], |
| 129 | + ) |
| 130 | + prefer_response_metrics: bool = Field( |
| 131 | + default=True, |
| 132 | + description=( |
| 133 | + "Prioritize server-reported metrics over client-calculated metrics " |
| 134 | + "when both are available." |
| 135 | + ), |
| 136 | + ) |
| 137 | + |
| 138 | + |
80 | 139 | class BenchmarkArgs(ReloadableBaseModel): |
81 | 140 | """Common benchmark configuration arguments.""" |
82 | 141 |
|
@@ -164,6 +223,11 @@ class BenchmarkArgs(ReloadableBaseModel): |
164 | 223 | ], |
165 | 224 | json_schema_extra={"argument_alias": "output"}, |
166 | 225 | ) |
| 226 | + metrics: MetricsArgs = Field( # type: ignore[assignment] |
| 227 | + default_factory=lambda: default_kind("generative"), |
| 228 | + description="Configuration for metrics collection and request sampling.", |
| 229 | + json_schema_extra={"argument_alias": "metrics"}, |
| 230 | + ) |
167 | 231 |
|
168 | 232 |
|
169 | 233 | class BenchmarkMetadata(StandardBaseModel): |
|
0 commit comments