|
16 | 16 | """Tests for benchmark CLI models, config building, and command handlers.""" |
17 | 17 |
|
18 | 18 | import asyncio |
| 19 | +import dataclasses |
19 | 20 | import io |
20 | 21 | import json |
| 22 | +import logging |
21 | 23 | import random |
22 | 24 | import tempfile |
23 | 25 | from pathlib import Path |
@@ -1195,6 +1197,151 @@ def test_accuracy_drain_timeout_defaults_to_unbounded( |
1195 | 1197 | acc = next(p for p in phases if p.phase_type == PhaseType.ACCURACY) |
1196 | 1198 | assert acc.drain_timeout is None |
1197 | 1199 |
|
| 1200 | + @pytest.mark.unit |
| 1201 | + def test_accuracy_phase_inherits_perf_concurrency( |
| 1202 | + self, base_rt_settings, simple_dataset |
| 1203 | + ): |
| 1204 | + """When the perf phase runs CONCURRENCY, the accuracy phase mirrors the |
| 1205 | + same fixed concurrency instead of bursting at MAX_THROUGHPUT.""" |
| 1206 | + rt = dataclasses.replace( |
| 1207 | + base_rt_settings, |
| 1208 | + load_pattern=LoadPattern( |
| 1209 | + type=LoadPatternType.CONCURRENCY, target_concurrency=7 |
| 1210 | + ), |
| 1211 | + ) |
| 1212 | + config = OfflineConfig(**_OFFLINE_KWARGS) |
| 1213 | + ctx = self._make_ctx(config, rt, simple_dataset) |
| 1214 | + ctx.eval_configs = [self._make_eval_config(simple_dataset)] |
| 1215 | + phases = _build_phases(ctx) |
| 1216 | + |
| 1217 | + acc = next(p for p in phases if p.phase_type == PhaseType.ACCURACY) |
| 1218 | + assert acc.runtime_settings.load_pattern is not None |
| 1219 | + assert acc.runtime_settings.load_pattern.type == LoadPatternType.CONCURRENCY |
| 1220 | + assert acc.runtime_settings.load_pattern.target_concurrency == 7 |
| 1221 | + |
| 1222 | + @pytest.mark.unit |
| 1223 | + def test_accuracy_phase_inherits_perf_poisson( |
| 1224 | + self, base_rt_settings, simple_dataset |
| 1225 | + ): |
| 1226 | + """POISSON perf: accuracy mirrors the same POISSON config (target_qps).""" |
| 1227 | + rt = dataclasses.replace( |
| 1228 | + base_rt_settings, |
| 1229 | + load_pattern=LoadPattern(type=LoadPatternType.POISSON, target_qps=10.0), |
| 1230 | + ) |
| 1231 | + config = OfflineConfig(**_OFFLINE_KWARGS) |
| 1232 | + ctx = self._make_ctx(config, rt, simple_dataset) |
| 1233 | + ctx.eval_configs = [self._make_eval_config(simple_dataset)] |
| 1234 | + phases = _build_phases(ctx) |
| 1235 | + |
| 1236 | + acc = next(p for p in phases if p.phase_type == PhaseType.ACCURACY) |
| 1237 | + assert acc.runtime_settings.load_pattern is not None |
| 1238 | + assert acc.runtime_settings.load_pattern.type == LoadPatternType.POISSON |
| 1239 | + assert acc.runtime_settings.load_pattern.target_qps == 10.0 |
| 1240 | + |
| 1241 | + @pytest.mark.unit |
| 1242 | + def test_accuracy_phase_max_throughput_when_perf_offline( |
| 1243 | + self, base_rt_settings, simple_dataset |
| 1244 | + ): |
| 1245 | + """Offline (MAX_THROUGHPUT) perf leaves accuracy at MAX_THROUGHPUT.""" |
| 1246 | + config = OfflineConfig(**_OFFLINE_KWARGS) |
| 1247 | + ctx = self._make_ctx(config, base_rt_settings, simple_dataset) |
| 1248 | + ctx.eval_configs = [self._make_eval_config(simple_dataset)] |
| 1249 | + phases = _build_phases(ctx) |
| 1250 | + |
| 1251 | + acc = next(p for p in phases if p.phase_type == PhaseType.ACCURACY) |
| 1252 | + assert acc.runtime_settings.load_pattern is not None |
| 1253 | + assert acc.runtime_settings.load_pattern.type == LoadPatternType.MAX_THROUGHPUT |
| 1254 | + |
| 1255 | + @pytest.mark.unit |
| 1256 | + def test_accuracy_phase_max_throughput_when_perf_agentic( |
| 1257 | + self, base_rt_settings, simple_dataset |
| 1258 | + ): |
| 1259 | + """AGENTIC_INFERENCE can't drive a non-agentic accuracy dataset, so the |
| 1260 | + accuracy phase falls back to MAX_THROUGHPUT instead of crashing.""" |
| 1261 | + rt = dataclasses.replace( |
| 1262 | + base_rt_settings, |
| 1263 | + load_pattern=LoadPattern( |
| 1264 | + type=LoadPatternType.AGENTIC_INFERENCE, target_concurrency=8 |
| 1265 | + ), |
| 1266 | + ) |
| 1267 | + config = OfflineConfig(**_OFFLINE_KWARGS) |
| 1268 | + ctx = self._make_ctx(config, rt, simple_dataset) |
| 1269 | + ctx.eval_configs = [self._make_eval_config(simple_dataset)] |
| 1270 | + phases = _build_phases(ctx) |
| 1271 | + |
| 1272 | + acc = next(p for p in phases if p.phase_type == PhaseType.ACCURACY) |
| 1273 | + assert acc.runtime_settings.load_pattern is not None |
| 1274 | + assert acc.runtime_settings.load_pattern.type == LoadPatternType.MAX_THROUGHPUT |
| 1275 | + |
| 1276 | + @pytest.mark.unit |
| 1277 | + def test_accuracy_phase_max_throughput_when_perf_none( |
| 1278 | + self, base_rt_settings, simple_dataset |
| 1279 | + ): |
| 1280 | + """A missing perf load pattern falls back to MAX_THROUGHPUT for accuracy.""" |
| 1281 | + rt = dataclasses.replace(base_rt_settings, load_pattern=None) |
| 1282 | + config = OfflineConfig(**_OFFLINE_KWARGS) |
| 1283 | + ctx = self._make_ctx(config, rt, simple_dataset) |
| 1284 | + ctx.eval_configs = [self._make_eval_config(simple_dataset)] |
| 1285 | + phases = _build_phases(ctx) |
| 1286 | + |
| 1287 | + acc = next(p for p in phases if p.phase_type == PhaseType.ACCURACY) |
| 1288 | + assert acc.runtime_settings.load_pattern is not None |
| 1289 | + assert acc.runtime_settings.load_pattern.type == LoadPatternType.MAX_THROUGHPUT |
| 1290 | + |
| 1291 | + @pytest.mark.unit |
| 1292 | + def test_accuracy_issuer_logs_load_mode( |
| 1293 | + self, base_rt_settings, simple_dataset, caplog |
| 1294 | + ): |
| 1295 | + """The accuracy issuer logs which load mode it will run in.""" |
| 1296 | + rt = dataclasses.replace( |
| 1297 | + base_rt_settings, |
| 1298 | + load_pattern=LoadPattern( |
| 1299 | + type=LoadPatternType.CONCURRENCY, target_concurrency=4 |
| 1300 | + ), |
| 1301 | + ) |
| 1302 | + config = OfflineConfig(**_OFFLINE_KWARGS) |
| 1303 | + ctx = self._make_ctx(config, rt, simple_dataset) |
| 1304 | + ctx.eval_configs = [self._make_eval_config(simple_dataset)] |
| 1305 | + |
| 1306 | + with caplog.at_level( |
| 1307 | + logging.INFO, logger="inference_endpoint.commands.benchmark.execute" |
| 1308 | + ): |
| 1309 | + _build_phases(ctx) |
| 1310 | + |
| 1311 | + msgs = [r.getMessage() for r in caplog.records] |
| 1312 | + assert any( |
| 1313 | + "load mode" in m and "concurrency" in m and "4" in m for m in msgs |
| 1314 | + ), msgs |
| 1315 | + |
| 1316 | + @pytest.mark.unit |
| 1317 | + def test_accuracy_issuer_logs_poisson_when_perf_poisson( |
| 1318 | + self, base_rt_settings, simple_dataset, caplog |
| 1319 | + ): |
| 1320 | + """POISSON perf logs the inherited poisson mode with its target_qps and |
| 1321 | + must NOT emit a target_concurrency suffix (the concurrency-only branch).""" |
| 1322 | + rt = dataclasses.replace( |
| 1323 | + base_rt_settings, |
| 1324 | + load_pattern=LoadPattern(type=LoadPatternType.POISSON, target_qps=10.0), |
| 1325 | + ) |
| 1326 | + config = OfflineConfig(**_OFFLINE_KWARGS) |
| 1327 | + ctx = self._make_ctx(config, rt, simple_dataset) |
| 1328 | + ctx.eval_configs = [self._make_eval_config(simple_dataset)] |
| 1329 | + |
| 1330 | + with caplog.at_level( |
| 1331 | + logging.INFO, logger="inference_endpoint.commands.benchmark.execute" |
| 1332 | + ): |
| 1333 | + _build_phases(ctx) |
| 1334 | + |
| 1335 | + msgs = [r.getMessage() for r in caplog.records] |
| 1336 | + load_mode_msgs = [m for m in msgs if "load mode" in m] |
| 1337 | + assert load_mode_msgs, msgs |
| 1338 | + assert any( |
| 1339 | + "poisson" in m and "target_qps=10.0" in m for m in load_mode_msgs |
| 1340 | + ), load_mode_msgs |
| 1341 | + assert all( |
| 1342 | + "target_concurrency" not in m for m in load_mode_msgs |
| 1343 | + ), load_mode_msgs |
| 1344 | + |
1198 | 1345 | @pytest.mark.unit |
1199 | 1346 | def test_warmup_uses_independent_rng_instances( |
1200 | 1347 | self, base_rt_settings, simple_dataset |
|
0 commit comments