Skip to content

Commit 55e518a

Browse files
authored
feat: accuracy issuer inherits perf concurrency in online mode (#357) (#379)
* feat: accuracy issuer inherits perf concurrency in online mode (#357) When the performance phase runs the CONCURRENCY load pattern (online), the accuracy phase now mirrors that same fixed concurrency instead of always bursting at MAX_THROUGHPUT, so evaluation exercises the endpoint the same way as the performance run. All other patterns are unchanged: POISSON and offline MAX_THROUGHPUT perf phases keep the accuracy phase at MAX_THROUGHPUT, since inheriting POISSON would silently rate-limit evaluation to the perf QPS (no accuracy QPS-budgeting yet). The gate is purely load_pattern.type == CONCURRENCY, which the schema already constrains to online mode. Also logs the accuracy issuer's chosen load mode (pattern + target_concurrency) per accuracy dataset. Adds unit tests for the concurrency-inheritance, POISSON-stays-max-throughput, offline-stays-max-throughput, and logging cases. Co-authored-by: Claude Opus 4.8 (10M context) <noreply@anthropic.com> --------- Signed-off-by: arekay-nv <230885705+arekay-nv@users.noreply.github.com>
1 parent b975e6c commit 55e518a

3 files changed

Lines changed: 176 additions & 6 deletions

File tree

src/inference_endpoint/commands/benchmark/execute.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,16 @@ def _build_phases(
562562
)
563563
)
564564

565+
# Accuracy mirrors the perf load pattern so evaluation exercises the
566+
# endpoint the same way it was benchmarked. AGENTIC_INFERENCE can't drive
567+
# the (non-agentic) accuracy datasets — create_load_strategy rejects it —
568+
# so it (and a missing perf pattern) falls back to MAX_THROUGHPUT.
569+
perf_lp = ctx.rt_settings.load_pattern if ctx.rt_settings is not None else None
570+
if perf_lp is None or perf_lp.type == LoadPatternType.AGENTIC_INFERENCE:
571+
acc_load_pattern = LoadPattern(type=LoadPatternType.MAX_THROUGHPUT)
572+
else:
573+
acc_load_pattern = perf_lp
574+
565575
# Accuracy phases — use eval_cfg.dataset_name as phase name so it matches
566576
# what Scorer._load_sample_index_map() looks up in sample_idx_map.json
567577
for eval_cfg in ctx.eval_configs:
@@ -574,15 +584,14 @@ def _build_phases(
574584
"AgenticInferenceDataset, which is not yet supported for "
575585
"accuracy evaluation."
576586
)
577-
# Accuracy phases run at MAX_THROUGHPUT; inheriting perf_lp (e.g. POISSON)
578-
# would silently rate-limit evaluation until an agentic inference accuracy strategy
579-
# and QPS-budgeting support are added.
587+
logger.info(
588+
"Accuracy issuer '%s' load mode: %s",
589+
eval_cfg.dataset_name,
590+
acc_load_pattern,
591+
)
580592
rng_settings = ctx.rt_settings or RuntimeSettings.from_config(
581593
ctx.config, acc_ds.num_samples()
582594
)
583-
acc_load_pattern: LoadPattern | None = LoadPattern(
584-
type=LoadPatternType.MAX_THROUGHPUT
585-
)
586595
acc_settings = RuntimeSettings(
587596
metric_target=rng_settings.metric_target,
588597
reported_metrics=rng_settings.reported_metrics,

src/inference_endpoint/config/schema.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,20 @@ def _validate_completeness(self) -> Self:
718718
)
719719
return self
720720

721+
def __str__(self) -> str:
722+
"""Human-readable "type (param=value)" form for logging, e.g.
723+
``concurrency (target_concurrency=7)`` / ``poisson (target_qps=10.0)``.
724+
Patterns without a driving parameter render as just the type name.
725+
"""
726+
if self.type in (
727+
LoadPatternType.CONCURRENCY,
728+
LoadPatternType.AGENTIC_INFERENCE,
729+
):
730+
return f"{self.type.value} (target_concurrency={self.target_concurrency})"
731+
if self.type == LoadPatternType.POISSON:
732+
return f"{self.type.value} (target_qps={self.target_qps})"
733+
return self.type.value
734+
721735

722736
@cyclopts.Parameter(name="*")
723737
class WarmupConfig(BaseModel):

tests/unit/commands/test_benchmark.py

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
"""Tests for benchmark CLI models, config building, and command handlers."""
1717

1818
import asyncio
19+
import dataclasses
1920
import io
2021
import json
22+
import logging
2123
import random
2224
import tempfile
2325
from pathlib import Path
@@ -1165,6 +1167,151 @@ def test_accuracy_drain_timeout_defaults_to_unbounded(
11651167
acc = next(p for p in phases if p.phase_type == PhaseType.ACCURACY)
11661168
assert acc.drain_timeout is None
11671169

1170+
@pytest.mark.unit
1171+
def test_accuracy_phase_inherits_perf_concurrency(
1172+
self, base_rt_settings, simple_dataset
1173+
):
1174+
"""When the perf phase runs CONCURRENCY, the accuracy phase mirrors the
1175+
same fixed concurrency instead of bursting at MAX_THROUGHPUT."""
1176+
rt = dataclasses.replace(
1177+
base_rt_settings,
1178+
load_pattern=LoadPattern(
1179+
type=LoadPatternType.CONCURRENCY, target_concurrency=7
1180+
),
1181+
)
1182+
config = OfflineConfig(**_OFFLINE_KWARGS)
1183+
ctx = self._make_ctx(config, rt, simple_dataset)
1184+
ctx.eval_configs = [self._make_eval_config(simple_dataset)]
1185+
phases = _build_phases(ctx)
1186+
1187+
acc = next(p for p in phases if p.phase_type == PhaseType.ACCURACY)
1188+
assert acc.runtime_settings.load_pattern is not None
1189+
assert acc.runtime_settings.load_pattern.type == LoadPatternType.CONCURRENCY
1190+
assert acc.runtime_settings.load_pattern.target_concurrency == 7
1191+
1192+
@pytest.mark.unit
1193+
def test_accuracy_phase_inherits_perf_poisson(
1194+
self, base_rt_settings, simple_dataset
1195+
):
1196+
"""POISSON perf: accuracy mirrors the same POISSON config (target_qps)."""
1197+
rt = dataclasses.replace(
1198+
base_rt_settings,
1199+
load_pattern=LoadPattern(type=LoadPatternType.POISSON, target_qps=10.0),
1200+
)
1201+
config = OfflineConfig(**_OFFLINE_KWARGS)
1202+
ctx = self._make_ctx(config, rt, simple_dataset)
1203+
ctx.eval_configs = [self._make_eval_config(simple_dataset)]
1204+
phases = _build_phases(ctx)
1205+
1206+
acc = next(p for p in phases if p.phase_type == PhaseType.ACCURACY)
1207+
assert acc.runtime_settings.load_pattern is not None
1208+
assert acc.runtime_settings.load_pattern.type == LoadPatternType.POISSON
1209+
assert acc.runtime_settings.load_pattern.target_qps == 10.0
1210+
1211+
@pytest.mark.unit
1212+
def test_accuracy_phase_max_throughput_when_perf_offline(
1213+
self, base_rt_settings, simple_dataset
1214+
):
1215+
"""Offline (MAX_THROUGHPUT) perf leaves accuracy at MAX_THROUGHPUT."""
1216+
config = OfflineConfig(**_OFFLINE_KWARGS)
1217+
ctx = self._make_ctx(config, base_rt_settings, simple_dataset)
1218+
ctx.eval_configs = [self._make_eval_config(simple_dataset)]
1219+
phases = _build_phases(ctx)
1220+
1221+
acc = next(p for p in phases if p.phase_type == PhaseType.ACCURACY)
1222+
assert acc.runtime_settings.load_pattern is not None
1223+
assert acc.runtime_settings.load_pattern.type == LoadPatternType.MAX_THROUGHPUT
1224+
1225+
@pytest.mark.unit
1226+
def test_accuracy_phase_max_throughput_when_perf_agentic(
1227+
self, base_rt_settings, simple_dataset
1228+
):
1229+
"""AGENTIC_INFERENCE can't drive a non-agentic accuracy dataset, so the
1230+
accuracy phase falls back to MAX_THROUGHPUT instead of crashing."""
1231+
rt = dataclasses.replace(
1232+
base_rt_settings,
1233+
load_pattern=LoadPattern(
1234+
type=LoadPatternType.AGENTIC_INFERENCE, target_concurrency=8
1235+
),
1236+
)
1237+
config = OfflineConfig(**_OFFLINE_KWARGS)
1238+
ctx = self._make_ctx(config, rt, simple_dataset)
1239+
ctx.eval_configs = [self._make_eval_config(simple_dataset)]
1240+
phases = _build_phases(ctx)
1241+
1242+
acc = next(p for p in phases if p.phase_type == PhaseType.ACCURACY)
1243+
assert acc.runtime_settings.load_pattern is not None
1244+
assert acc.runtime_settings.load_pattern.type == LoadPatternType.MAX_THROUGHPUT
1245+
1246+
@pytest.mark.unit
1247+
def test_accuracy_phase_max_throughput_when_perf_none(
1248+
self, base_rt_settings, simple_dataset
1249+
):
1250+
"""A missing perf load pattern falls back to MAX_THROUGHPUT for accuracy."""
1251+
rt = dataclasses.replace(base_rt_settings, load_pattern=None)
1252+
config = OfflineConfig(**_OFFLINE_KWARGS)
1253+
ctx = self._make_ctx(config, rt, simple_dataset)
1254+
ctx.eval_configs = [self._make_eval_config(simple_dataset)]
1255+
phases = _build_phases(ctx)
1256+
1257+
acc = next(p for p in phases if p.phase_type == PhaseType.ACCURACY)
1258+
assert acc.runtime_settings.load_pattern is not None
1259+
assert acc.runtime_settings.load_pattern.type == LoadPatternType.MAX_THROUGHPUT
1260+
1261+
@pytest.mark.unit
1262+
def test_accuracy_issuer_logs_load_mode(
1263+
self, base_rt_settings, simple_dataset, caplog
1264+
):
1265+
"""The accuracy issuer logs which load mode it will run in."""
1266+
rt = dataclasses.replace(
1267+
base_rt_settings,
1268+
load_pattern=LoadPattern(
1269+
type=LoadPatternType.CONCURRENCY, target_concurrency=4
1270+
),
1271+
)
1272+
config = OfflineConfig(**_OFFLINE_KWARGS)
1273+
ctx = self._make_ctx(config, rt, simple_dataset)
1274+
ctx.eval_configs = [self._make_eval_config(simple_dataset)]
1275+
1276+
with caplog.at_level(
1277+
logging.INFO, logger="inference_endpoint.commands.benchmark.execute"
1278+
):
1279+
_build_phases(ctx)
1280+
1281+
msgs = [r.getMessage() for r in caplog.records]
1282+
assert any(
1283+
"load mode" in m and "concurrency" in m and "4" in m for m in msgs
1284+
), msgs
1285+
1286+
@pytest.mark.unit
1287+
def test_accuracy_issuer_logs_poisson_when_perf_poisson(
1288+
self, base_rt_settings, simple_dataset, caplog
1289+
):
1290+
"""POISSON perf logs the inherited poisson mode with its target_qps and
1291+
must NOT emit a target_concurrency suffix (the concurrency-only branch)."""
1292+
rt = dataclasses.replace(
1293+
base_rt_settings,
1294+
load_pattern=LoadPattern(type=LoadPatternType.POISSON, target_qps=10.0),
1295+
)
1296+
config = OfflineConfig(**_OFFLINE_KWARGS)
1297+
ctx = self._make_ctx(config, rt, simple_dataset)
1298+
ctx.eval_configs = [self._make_eval_config(simple_dataset)]
1299+
1300+
with caplog.at_level(
1301+
logging.INFO, logger="inference_endpoint.commands.benchmark.execute"
1302+
):
1303+
_build_phases(ctx)
1304+
1305+
msgs = [r.getMessage() for r in caplog.records]
1306+
load_mode_msgs = [m for m in msgs if "load mode" in m]
1307+
assert load_mode_msgs, msgs
1308+
assert any(
1309+
"poisson" in m and "target_qps=10.0" in m for m in load_mode_msgs
1310+
), load_mode_msgs
1311+
assert all(
1312+
"target_concurrency" not in m for m in load_mode_msgs
1313+
), load_mode_msgs
1314+
11681315
@pytest.mark.unit
11691316
def test_warmup_uses_independent_rng_instances(
11701317
self, base_rt_settings, simple_dataset

0 commit comments

Comments
 (0)