[SPEC] feat: add adaptive speculative decoding metrics (#25940)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Jarrod Barnes <jbarnes850@gmail.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
Jarrod Barnes
parent
106092123f
commit
a0670b5ba3
@@ -79,6 +79,7 @@ class TestAdaptiveSpeculativeServer(CustomTestCase):
|
||||
"--speculative-adaptive",
|
||||
"--speculative-adaptive-config",
|
||||
cls.adaptive_config_path,
|
||||
"--enable-metrics",
|
||||
"--skip-server-warmup",
|
||||
"--mem-fraction-static",
|
||||
"0.7",
|
||||
@@ -100,6 +101,24 @@ class TestAdaptiveSpeculativeServer(CustomTestCase):
|
||||
self.assertEqual(response.status_code, 200, response.text)
|
||||
return response.json()["internal_states"][0]
|
||||
|
||||
def _scrape_metric(self, name: str, **label_filter) -> float | None:
|
||||
"""Return the value of a Prometheus sample line, or None if absent.
|
||||
|
||||
Matches a line whose metric name is exactly *name* (next char is '{'
|
||||
or whitespace) and whose labels include every key=value in
|
||||
*label_filter*.
|
||||
"""
|
||||
text = requests.get(self.base_url + "/metrics", timeout=30).text
|
||||
for line in text.splitlines():
|
||||
if line.startswith("#") or not line.startswith(name):
|
||||
continue
|
||||
rest = line[len(name) :]
|
||||
if rest and rest[0] not in "{ ":
|
||||
continue
|
||||
if all(f'{k}="{v}"' in line for k, v in label_filter.items()):
|
||||
return float(line.rsplit(" ", 1)[1])
|
||||
return None
|
||||
|
||||
def _generate(self, prompt: str, max_new_tokens: int = 64) -> dict:
|
||||
response = requests.post(
|
||||
self.base_url + "/generate",
|
||||
@@ -165,6 +184,21 @@ class TestAdaptiveSpeculativeServer(CustomTestCase):
|
||||
avg_accept_len = server_info["internal_states"][0]["avg_spec_accept_length"]
|
||||
print(f"avg_spec_accept_length={avg_accept_len:.4f}")
|
||||
|
||||
def test_adaptive_metrics_exposed(self):
|
||||
"""After an upshift, the adaptive current-state gauges are scrapeable."""
|
||||
state = self._drive_upshift()
|
||||
self.assertEqual(state["speculative_num_steps"], 3, f"Never upshifted: {state}")
|
||||
# One more decode so the reporter emits a fresh logging interval.
|
||||
self._generate(HIGH_ACCEPT_PROMPT)
|
||||
|
||||
steps = self._scrape_metric("sglang:spec_num_steps")
|
||||
draft_tokens = self._scrape_metric("sglang:spec_num_draft_tokens")
|
||||
|
||||
self.assertEqual(steps, 3.0, "spec_num_steps gauge missing or wrong")
|
||||
self.assertEqual(
|
||||
draft_tokens, 4.0, "spec_num_draft_tokens gauge missing or wrong"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user