guidellm 0.3.1__py3-none-any.whl → 0.6.0a5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. guidellm/__init__.py +5 -2
  2. guidellm/__main__.py +524 -255
  3. guidellm/backends/__init__.py +33 -0
  4. guidellm/backends/backend.py +109 -0
  5. guidellm/backends/openai.py +340 -0
  6. guidellm/backends/response_handlers.py +428 -0
  7. guidellm/benchmark/__init__.py +69 -39
  8. guidellm/benchmark/benchmarker.py +160 -316
  9. guidellm/benchmark/entrypoints.py +560 -127
  10. guidellm/benchmark/outputs/__init__.py +24 -0
  11. guidellm/benchmark/outputs/console.py +633 -0
  12. guidellm/benchmark/outputs/csv.py +721 -0
  13. guidellm/benchmark/outputs/html.py +473 -0
  14. guidellm/benchmark/outputs/output.py +169 -0
  15. guidellm/benchmark/outputs/serialized.py +69 -0
  16. guidellm/benchmark/profiles.py +718 -0
  17. guidellm/benchmark/progress.py +553 -556
  18. guidellm/benchmark/scenarios/__init__.py +40 -0
  19. guidellm/benchmark/scenarios/chat.json +6 -0
  20. guidellm/benchmark/scenarios/rag.json +6 -0
  21. guidellm/benchmark/schemas/__init__.py +66 -0
  22. guidellm/benchmark/schemas/base.py +402 -0
  23. guidellm/benchmark/schemas/generative/__init__.py +55 -0
  24. guidellm/benchmark/schemas/generative/accumulator.py +841 -0
  25. guidellm/benchmark/schemas/generative/benchmark.py +163 -0
  26. guidellm/benchmark/schemas/generative/entrypoints.py +381 -0
  27. guidellm/benchmark/schemas/generative/metrics.py +927 -0
  28. guidellm/benchmark/schemas/generative/report.py +158 -0
  29. guidellm/data/__init__.py +34 -4
  30. guidellm/data/builders.py +541 -0
  31. guidellm/data/collators.py +16 -0
  32. guidellm/data/config.py +120 -0
  33. guidellm/data/deserializers/__init__.py +49 -0
  34. guidellm/data/deserializers/deserializer.py +141 -0
  35. guidellm/data/deserializers/file.py +223 -0
  36. guidellm/data/deserializers/huggingface.py +94 -0
  37. guidellm/data/deserializers/memory.py +194 -0
  38. guidellm/data/deserializers/synthetic.py +246 -0
  39. guidellm/data/entrypoints.py +52 -0
  40. guidellm/data/loaders.py +190 -0
  41. guidellm/data/preprocessors/__init__.py +27 -0
  42. guidellm/data/preprocessors/formatters.py +410 -0
  43. guidellm/data/preprocessors/mappers.py +196 -0
  44. guidellm/data/preprocessors/preprocessor.py +30 -0
  45. guidellm/data/processor.py +29 -0
  46. guidellm/data/schemas.py +175 -0
  47. guidellm/data/utils/__init__.py +6 -0
  48. guidellm/data/utils/dataset.py +94 -0
  49. guidellm/extras/__init__.py +4 -0
  50. guidellm/extras/audio.py +220 -0
  51. guidellm/extras/vision.py +242 -0
  52. guidellm/logger.py +2 -2
  53. guidellm/mock_server/__init__.py +8 -0
  54. guidellm/mock_server/config.py +84 -0
  55. guidellm/mock_server/handlers/__init__.py +17 -0
  56. guidellm/mock_server/handlers/chat_completions.py +280 -0
  57. guidellm/mock_server/handlers/completions.py +280 -0
  58. guidellm/mock_server/handlers/tokenizer.py +142 -0
  59. guidellm/mock_server/models.py +510 -0
  60. guidellm/mock_server/server.py +238 -0
  61. guidellm/mock_server/utils.py +302 -0
  62. guidellm/scheduler/__init__.py +69 -26
  63. guidellm/scheduler/constraints/__init__.py +49 -0
  64. guidellm/scheduler/constraints/constraint.py +325 -0
  65. guidellm/scheduler/constraints/error.py +411 -0
  66. guidellm/scheduler/constraints/factory.py +182 -0
  67. guidellm/scheduler/constraints/request.py +312 -0
  68. guidellm/scheduler/constraints/saturation.py +722 -0
  69. guidellm/scheduler/environments.py +252 -0
  70. guidellm/scheduler/scheduler.py +137 -368
  71. guidellm/scheduler/schemas.py +358 -0
  72. guidellm/scheduler/strategies.py +617 -0
  73. guidellm/scheduler/worker.py +413 -419
  74. guidellm/scheduler/worker_group.py +712 -0
  75. guidellm/schemas/__init__.py +65 -0
  76. guidellm/schemas/base.py +417 -0
  77. guidellm/schemas/info.py +188 -0
  78. guidellm/schemas/request.py +235 -0
  79. guidellm/schemas/request_stats.py +349 -0
  80. guidellm/schemas/response.py +124 -0
  81. guidellm/schemas/statistics.py +1018 -0
  82. guidellm/{config.py → settings.py} +31 -24
  83. guidellm/utils/__init__.py +71 -8
  84. guidellm/utils/auto_importer.py +98 -0
  85. guidellm/utils/cli.py +132 -5
  86. guidellm/utils/console.py +566 -0
  87. guidellm/utils/encoding.py +778 -0
  88. guidellm/utils/functions.py +159 -0
  89. guidellm/utils/hf_datasets.py +1 -2
  90. guidellm/utils/hf_transformers.py +4 -4
  91. guidellm/utils/imports.py +9 -0
  92. guidellm/utils/messaging.py +1118 -0
  93. guidellm/utils/mixins.py +115 -0
  94. guidellm/utils/random.py +3 -4
  95. guidellm/utils/registry.py +220 -0
  96. guidellm/utils/singleton.py +133 -0
  97. guidellm/utils/synchronous.py +159 -0
  98. guidellm/utils/text.py +163 -50
  99. guidellm/utils/typing.py +41 -0
  100. guidellm/version.py +2 -2
  101. guidellm-0.6.0a5.dist-info/METADATA +364 -0
  102. guidellm-0.6.0a5.dist-info/RECORD +109 -0
  103. guidellm/backend/__init__.py +0 -23
  104. guidellm/backend/backend.py +0 -259
  105. guidellm/backend/openai.py +0 -708
  106. guidellm/backend/response.py +0 -136
  107. guidellm/benchmark/aggregator.py +0 -760
  108. guidellm/benchmark/benchmark.py +0 -837
  109. guidellm/benchmark/output.py +0 -997
  110. guidellm/benchmark/profile.py +0 -409
  111. guidellm/benchmark/scenario.py +0 -104
  112. guidellm/data/prideandprejudice.txt.gz +0 -0
  113. guidellm/dataset/__init__.py +0 -22
  114. guidellm/dataset/creator.py +0 -213
  115. guidellm/dataset/entrypoints.py +0 -42
  116. guidellm/dataset/file.py +0 -92
  117. guidellm/dataset/hf_datasets.py +0 -62
  118. guidellm/dataset/in_memory.py +0 -132
  119. guidellm/dataset/synthetic.py +0 -287
  120. guidellm/objects/__init__.py +0 -18
  121. guidellm/objects/pydantic.py +0 -89
  122. guidellm/objects/statistics.py +0 -953
  123. guidellm/preprocess/__init__.py +0 -3
  124. guidellm/preprocess/dataset.py +0 -374
  125. guidellm/presentation/__init__.py +0 -28
  126. guidellm/presentation/builder.py +0 -27
  127. guidellm/presentation/data_models.py +0 -232
  128. guidellm/presentation/injector.py +0 -66
  129. guidellm/request/__init__.py +0 -18
  130. guidellm/request/loader.py +0 -284
  131. guidellm/request/request.py +0 -79
  132. guidellm/request/types.py +0 -10
  133. guidellm/scheduler/queues.py +0 -25
  134. guidellm/scheduler/result.py +0 -155
  135. guidellm/scheduler/strategy.py +0 -495
  136. guidellm-0.3.1.dist-info/METADATA +0 -329
  137. guidellm-0.3.1.dist-info/RECORD +0 -62
  138. {guidellm-0.3.1.dist-info → guidellm-0.6.0a5.dist-info}/WHEEL +0 -0
  139. {guidellm-0.3.1.dist-info → guidellm-0.6.0a5.dist-info}/entry_points.txt +0 -0
  140. {guidellm-0.3.1.dist-info → guidellm-0.6.0a5.dist-info}/licenses/LICENSE +0 -0
  141. {guidellm-0.3.1.dist-info → guidellm-0.6.0a5.dist-info}/top_level.txt +0 -0
@@ -1,334 +1,178 @@
1
- import time
2
- import uuid
3
- from abc import ABC, abstractmethod
4
- from collections.abc import AsyncGenerator, Iterable
5
- from pathlib import Path
6
- from typing import (
7
- Any,
8
- Generic,
9
- Literal,
10
- Optional,
11
- Union,
12
- )
1
+ """
2
+ Benchmark execution orchestration and lifecycle management.
3
+
4
+ Provides the core benchmarking engine that coordinates request scheduling,
5
+ data aggregation, and result compilation across execution strategies and
6
+ environments. The Benchmarker manages the complete benchmark lifecycle from
7
+ request submission through result compilation while implementing thread-safe
8
+ singleton operations for consistent state management across concurrent workflows.
9
+ """
13
10
 
14
- from pydantic import Field
15
- from transformers import PreTrainedTokenizerBase # type: ignore # noqa: PGH003
11
+ from __future__ import annotations
16
12
 
17
- from guidellm.backend import Backend, ResponseSummary
18
- from guidellm.benchmark.aggregator import (
19
- AggregatorT,
13
+ import uuid
14
+ from abc import ABC
15
+ from collections.abc import AsyncIterator, Iterable
16
+ from typing import Generic
17
+
18
+ from guidellm.benchmark.profiles import Profile
19
+ from guidellm.benchmark.progress import BenchmarkerProgress
20
+ from guidellm.benchmark.schemas import (
21
+ BenchmarkAccumulatorT,
22
+ BenchmarkConfig,
20
23
  BenchmarkT,
21
- GenerativeBenchmarkAggregator,
22
24
  )
23
- from guidellm.benchmark.benchmark import BenchmarkArgs, GenerativeBenchmark
24
- from guidellm.benchmark.profile import Profile
25
- from guidellm.objects import StandardBaseModel
26
- from guidellm.request import (
27
- GenerationRequest,
28
- GenerativeRequestLoaderDescription,
29
- RequestLoaderDescription,
25
+ from guidellm.benchmark.schemas.base import TransientPhaseConfig
26
+ from guidellm.logger import logger
27
+ from guidellm.scheduler import (
28
+ BackendInterface,
29
+ Constraint,
30
+ Environment,
31
+ MultiTurnRequestT,
30
32
  RequestT,
31
33
  ResponseT,
32
- )
33
- from guidellm.scheduler import (
34
- GenerativeRequestsWorker,
35
- RequestsWorker,
36
34
  Scheduler,
37
- SchedulerRequestResult,
38
35
  SchedulingStrategy,
39
36
  )
37
+ from guidellm.utils import ThreadSafeSingletonMixin
38
+ from guidellm.utils.mixins import InfoMixin
40
39
 
41
- __all__ = ["Benchmarker", "BenchmarkerResult", "GenerativeBenchmarker"]
40
+ __all__ = ["Benchmarker"]
42
41
 
43
42
 
44
- class BenchmarkerResult(
45
- StandardBaseModel, Generic[AggregatorT, BenchmarkT, RequestT, ResponseT]
43
+ class Benchmarker(
44
+ Generic[BenchmarkT, RequestT, ResponseT],
45
+ ABC,
46
+ ThreadSafeSingletonMixin,
46
47
  ):
47
- type_: Literal[
48
- "run_start",
49
- "run_complete",
50
- "scheduler_start",
51
- "scheduler_update",
52
- "scheduler_complete",
53
- "benchmark_compiled",
54
- ]
55
- start_time: float
56
- end_number: int
57
- profile: Profile
58
- current_index: int
59
- current_strategy: Optional[SchedulingStrategy] = None
60
- current_aggregator: Optional[AggregatorT] = None
61
- current_benchmark: Optional[BenchmarkT] = None
62
- current_result: Optional[SchedulerRequestResult[RequestT, ResponseT]] = None
63
-
64
-
65
- class BenchmarkerStrategyLimits(StandardBaseModel):
66
- requests_loader_size: Optional[int] = Field(
67
- description="Size of the request loader.",
68
- )
69
- max_number_per_strategy: Optional[int] = Field(
70
- description="Maximum number of requests to process per strategy.",
71
- ge=0,
72
- )
73
- max_duration_per_strategy: Optional[float] = Field(
74
- description="Maximum duration (in seconds) to process requests per strategy.",
75
- ge=0,
76
- )
77
- warmup_percent_per_strategy: Optional[float] = Field(
78
- description="Percentage of requests to use for warmup.",
79
- ge=0,
80
- le=1,
81
- )
82
- cooldown_percent_per_strategy: Optional[float] = Field(
83
- description="Percentage of requests to use for cooldown.",
84
- ge=0,
85
- le=1,
86
- )
87
-
88
- @property
89
- def max_number(self) -> Optional[int]:
90
- if self.max_number_per_strategy is not None:
91
- return self.max_number_per_strategy
92
-
93
- if self.requests_loader_size is not None:
94
- return self.requests_loader_size
95
-
96
- return None
97
-
98
- @property
99
- def max_duration(self) -> Optional[float]:
100
- return self.max_duration_per_strategy
101
-
102
- @property
103
- def warmup_number(self) -> Optional[int]:
104
- if self.warmup_percent_per_strategy is None or self.max_number is None:
105
- return None
106
-
107
- return int(self.warmup_percent_per_strategy * self.max_number)
108
-
109
- @property
110
- def warmup_duration(self) -> Optional[float]:
111
- if self.warmup_percent_per_strategy is None or self.max_duration is None:
112
- return None
113
-
114
- return self.warmup_percent_per_strategy * self.max_duration
115
-
116
- @property
117
- def cooldown_number(self) -> Optional[int]:
118
- if self.cooldown_percent_per_strategy is None or self.max_number is None:
119
- return None
120
-
121
- return int(self.cooldown_percent_per_strategy * self.max_number)
122
-
123
- @property
124
- def cooldown_duration(self) -> Optional[float]:
125
- if self.cooldown_percent_per_strategy is None or self.max_duration is None:
126
- return None
127
-
128
- return self.cooldown_percent_per_strategy * self.max_duration
48
+ """
49
+ Orchestrates benchmark execution across scheduling strategies.
129
50
 
130
-
131
- class Benchmarker(Generic[AggregatorT, BenchmarkT, RequestT, ResponseT], ABC):
132
- def __init__(
133
- self,
134
- worker: RequestsWorker[RequestT, ResponseT],
135
- request_loader: Iterable[RequestT],
136
- requests_loader_description: RequestLoaderDescription,
137
- benchmark_save_extras: Optional[dict[str, Any]] = None,
138
- ):
139
- self.worker = worker
140
- self.scheduler: Scheduler[RequestT, ResponseT] = Scheduler(
141
- worker=worker, request_loader=request_loader
142
- )
143
- self.requests_loader_description = requests_loader_description
144
- self.benchmark_save_extras = benchmark_save_extras
51
+ Coordinates benchmarking runs by managing request scheduling, metric aggregation,
52
+ and result compilation. Implements a thread-safe singleton pattern to ensure
53
+ consistent state management across concurrent operations while supporting multiple
54
+ scheduling strategies and execution environments.
55
+ """
145
56
 
146
57
  async def run(
147
58
  self,
59
+ accumulator_class: type[BenchmarkAccumulatorT],
60
+ benchmark_class: type[BenchmarkT],
61
+ requests: Iterable[RequestT | MultiTurnRequestT[RequestT]],
62
+ backend: BackendInterface[RequestT, ResponseT],
148
63
  profile: Profile,
149
- max_number_per_strategy: Optional[int],
150
- max_duration_per_strategy: Optional[float],
151
- warmup_percent_per_strategy: Optional[float],
152
- cooldown_percent_per_strategy: Optional[float],
153
- ) -> AsyncGenerator[
154
- BenchmarkerResult[AggregatorT, BenchmarkT, RequestT, ResponseT], None
155
- ]:
156
- try:
157
- requests_loader_size = len(self.scheduler.request_loader) # type: ignore[arg-type]
158
- except Exception: # noqa: BLE001
159
- requests_loader_size = None
160
-
161
- strategy_limits = BenchmarkerStrategyLimits(
162
- requests_loader_size=requests_loader_size,
163
- max_number_per_strategy=max_number_per_strategy,
164
- max_duration_per_strategy=max_duration_per_strategy,
165
- warmup_percent_per_strategy=warmup_percent_per_strategy,
166
- cooldown_percent_per_strategy=cooldown_percent_per_strategy,
167
- )
168
- start_time = time.time()
169
- end_number = len(profile.strategy_types)
170
- current_index = -1
171
- run_id = str(uuid.uuid4())
172
-
173
- yield BenchmarkerResult(
174
- type_="run_start",
175
- start_time=start_time,
176
- end_number=end_number,
177
- profile=profile,
178
- current_index=current_index,
179
- current_strategy=None,
180
- current_aggregator=None,
181
- current_benchmark=None,
182
- current_result=None,
183
- )
184
-
185
- while scheduling_strategy := profile.next_strategy():
186
- current_index += 1
187
- aggregator = self.create_benchmark_aggregator(
188
- run_id=run_id,
189
- profile=profile,
190
- strategy_index=current_index,
191
- strategy=scheduling_strategy,
192
- limits=strategy_limits,
193
- )
194
-
195
- async for result in self.scheduler.run(
196
- scheduling_strategy=scheduling_strategy,
197
- max_number=max_number_per_strategy,
198
- max_duration=max_duration_per_strategy,
199
- ):
200
- if result.type_ == "run_start":
201
- yield BenchmarkerResult(
202
- type_="scheduler_start",
203
- start_time=start_time,
204
- end_number=end_number,
205
- profile=profile,
206
- current_index=current_index,
207
- current_strategy=scheduling_strategy,
208
- current_aggregator=aggregator,
209
- current_benchmark=None,
210
- current_result=None,
211
- )
212
- elif result.type_ == "run_complete":
213
- yield BenchmarkerResult(
214
- type_="scheduler_complete",
215
- start_time=start_time,
216
- end_number=end_number,
217
- profile=profile,
218
- current_index=current_index,
219
- current_strategy=scheduling_strategy,
220
- current_aggregator=aggregator,
221
- current_benchmark=None,
222
- current_result=None,
223
- )
224
- elif isinstance(result, SchedulerRequestResult):
225
- aggregator.add_result(result)
226
-
227
- yield BenchmarkerResult(
228
- type_="scheduler_update",
229
- start_time=start_time,
230
- end_number=end_number,
231
- profile=profile,
232
- current_index=current_index,
233
- current_strategy=scheduling_strategy,
234
- current_aggregator=aggregator,
235
- current_benchmark=None,
236
- current_result=result,
237
- )
238
- else:
239
- raise ValueError(f"Unexpected result type: {type(result)}")
240
-
241
- benchmark: BenchmarkT = aggregator.compile()
242
- profile.completed_strategy(
243
- average_rate=benchmark.metrics.requests_per_second.successful.mean,
244
- average_concurrency=benchmark.metrics.request_concurrency.successful.mean,
245
- )
246
-
247
- yield BenchmarkerResult(
248
- type_="benchmark_compiled",
249
- start_time=start_time,
250
- end_number=end_number,
251
- profile=profile,
252
- current_index=current_index,
253
- current_strategy=scheduling_strategy,
254
- current_aggregator=None,
255
- current_benchmark=benchmark,
256
- current_result=None,
257
- )
258
-
259
- yield BenchmarkerResult(
260
- type_="run_complete",
261
- start_time=start_time,
262
- end_number=end_number,
263
- profile=profile,
264
- current_index=current_index,
265
- current_strategy=None,
266
- current_aggregator=None,
267
- current_benchmark=None,
268
- current_result=None,
269
- )
270
-
271
- @abstractmethod
272
- def create_benchmark_aggregator(
273
- self,
274
- run_id: str,
275
- profile: Profile,
276
- strategy_index: int,
277
- strategy: SchedulingStrategy,
278
- limits: BenchmarkerStrategyLimits,
279
- ) -> AggregatorT: ...
280
-
281
-
282
- class GenerativeBenchmarker(
283
- Benchmarker[
284
- GenerativeBenchmarkAggregator,
285
- GenerativeBenchmark,
286
- GenerationRequest,
287
- ResponseSummary,
288
- ],
289
- ):
290
- def __init__(
291
- self,
292
- backend: Backend,
293
- request_loader: Iterable[GenerationRequest],
294
- request_loader_description: GenerativeRequestLoaderDescription,
295
- benchmark_save_extras: Optional[dict[str, Any]] = None,
296
- processor: Optional[Union[str, Path, PreTrainedTokenizerBase]] = None,
297
- processor_args: Optional[dict[str, Any]] = None,
298
- ):
299
- super().__init__(
300
- worker=GenerativeRequestsWorker(backend),
301
- request_loader=request_loader,
302
- requests_loader_description=request_loader_description,
303
- benchmark_save_extras=benchmark_save_extras,
304
- )
305
- self.processor = processor
306
- self.processor_args = processor_args
307
-
308
- def create_benchmark_aggregator(
309
- self,
310
- run_id: str,
311
- profile: Profile,
312
- strategy_index: int,
313
- strategy: SchedulingStrategy,
314
- limits: BenchmarkerStrategyLimits,
315
- ) -> GenerativeBenchmarkAggregator:
316
- return GenerativeBenchmarkAggregator(
317
- run_id=run_id,
318
- args=BenchmarkArgs(
319
- profile=profile,
320
- strategy_index=strategy_index,
321
- strategy=strategy,
322
- max_number=limits.max_number,
323
- max_duration=limits.max_duration,
324
- warmup_number=limits.warmup_number,
325
- warmup_duration=limits.warmup_duration,
326
- cooldown_number=limits.cooldown_number,
327
- cooldown_duration=limits.cooldown_duration,
328
- ),
329
- worker_description=self.worker.description, # type: ignore[arg-type]
330
- request_loader_description=self.requests_loader_description, # type: ignore[arg-type]
331
- extras=self.benchmark_save_extras or {},
332
- processor=self.processor,
333
- processor_args=self.processor_args,
334
- )
64
+ environment: Environment,
65
+ warmup: TransientPhaseConfig,
66
+ cooldown: TransientPhaseConfig,
67
+ sample_requests: int | None = 20,
68
+ prefer_response_metrics: bool = True,
69
+ progress: (
70
+ BenchmarkerProgress[BenchmarkAccumulatorT, BenchmarkT] | None
71
+ ) = None,
72
+ ) -> AsyncIterator[BenchmarkT]:
73
+ """
74
+ Execute benchmark runs across scheduling strategies in the profile.
75
+
76
+ :param accumulator_class: Class for accumulating metrics during execution
77
+ :param benchmark_class: Class for constructing final benchmark results
78
+ :param requests: Request datasets to process across strategies
79
+ :param backend: Backend interface for executing requests
80
+ :param profile: Profile defining scheduling strategies and constraints
81
+ :param environment: Environment for execution coordination
82
+ :param warmup: Warmup phase configuration before benchmarking
83
+ :param cooldown: Cooldown phase configuration after benchmarking
84
+ :param sample_requests: Number of requests to sample for estimation,
85
+ defaults to 20
86
+ :param prefer_response_metrics: Whether to prefer response metrics over
87
+ request metrics, defaults to True
88
+ :param progress: Optional tracker for benchmark lifecycle events
89
+ :yield: Compiled benchmark result for each strategy execution
90
+ :raises Exception: If benchmark execution or compilation fails
91
+ """
92
+ with self.thread_lock:
93
+ if progress:
94
+ await progress.on_initialize(profile)
95
+
96
+ run_id = str(uuid.uuid4())
97
+ strategies_generator = profile.strategies_generator()
98
+ strategy: SchedulingStrategy | None
99
+ constraints: dict[str, Constraint] | None
100
+ strategy, constraints = next(strategies_generator)
101
+
102
+ while strategy is not None:
103
+ if progress:
104
+ await progress.on_benchmark_start(strategy)
105
+
106
+ config = BenchmarkConfig(
107
+ run_id=run_id,
108
+ run_index=len(profile.completed_strategies),
109
+ strategy=strategy,
110
+ constraints=(
111
+ {
112
+ key: InfoMixin.extract_from_obj(val)
113
+ for key, val in constraints.items()
114
+ }
115
+ if isinstance(constraints, dict)
116
+ else {"constraint": InfoMixin.extract_from_obj(constraints)}
117
+ if constraints
118
+ else {}
119
+ ),
120
+ sample_requests=sample_requests,
121
+ warmup=warmup,
122
+ cooldown=cooldown,
123
+ prefer_response_metrics=prefer_response_metrics,
124
+ profile=profile,
125
+ requests=InfoMixin.extract_from_obj(requests),
126
+ backend=InfoMixin.extract_from_obj(backend),
127
+ environment=InfoMixin.extract_from_obj(environment),
128
+ )
129
+ accumulator = accumulator_class(config=config)
130
+ scheduler_state = None
131
+ scheduler: Scheduler[RequestT, ResponseT] = Scheduler()
132
+
133
+ async for (
134
+ response,
135
+ request,
136
+ request_info,
137
+ scheduler_state,
138
+ ) in scheduler.run(
139
+ requests=requests,
140
+ backend=backend,
141
+ strategy=strategy,
142
+ env=environment,
143
+ **constraints or {},
144
+ ):
145
+ try:
146
+ accumulator.update_estimate(
147
+ response,
148
+ request,
149
+ request_info,
150
+ scheduler_state,
151
+ )
152
+ if progress:
153
+ await progress.on_benchmark_update(
154
+ accumulator, scheduler_state
155
+ )
156
+ except Exception as err: # noqa: BLE001
157
+ logger.error(
158
+ f"Error updating benchmark estimate/progress: {err}"
159
+ )
160
+
161
+ benchmark = benchmark_class.compile(
162
+ accumulator=accumulator,
163
+ scheduler_state=scheduler_state,
164
+ )
165
+
166
+ if progress:
167
+ await progress.on_benchmark_complete(benchmark)
168
+
169
+ yield benchmark
170
+
171
+ try:
172
+ strategy, constraints = strategies_generator.send(benchmark)
173
+ except StopIteration:
174
+ strategy = None
175
+ constraints = None
176
+
177
+ if progress:
178
+ await progress.on_finalize()