Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 85 additions & 1 deletion autoresearch/prefill/supervisor.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,76 @@ def parse_research_verdict(output: str, candidate_id: str) -> dict:
}


class StrategyPrefillHeartbeat:
def __init__(
self,
dashboard: str = "http://127.0.0.1:8090",
interval_s: float = 10.0,
) -> None:
self.dashboard = dashboard.rstrip("/")
self.interval_s = interval_s
self._stop = threading.Event()
self._thread: threading.Thread | None = None
self._baseline: dict = {}
self._last: tuple | None = None

def __enter__(self):
try:
self._baseline = _json_request(
f"{self.dashboard}/v1/network/summary",
).get("prefill", {})
except Exception as exc:
print(
"[autoresearch] Strategy Prefill telemetry warning: "
f"{type(exc).__name__}: {exc}",
flush=True,
)
self._thread = threading.Thread(target=self._run, daemon=True)
self._thread.start()
return self

def __exit__(self, *_exc) -> None:
self._stop.set()
if self._thread is not None:
self._thread.join(timeout=self.interval_s + 2)
self._emit()

def _run(self) -> None:
while not self._stop.wait(self.interval_s):
self._emit()

def _delta(self, current: dict, name: str) -> int:
return max(
0,
int(current.get(name, 0)) - int(self._baseline.get(name, 0)),
)

def _emit(self) -> None:
try:
current = _json_request(
f"{self.dashboard}/v1/network/summary",
).get("prefill", {})
except Exception:
return
total = self._delta(current, "remote_job_tokens_total")
computed = self._delta(current, "remote_job_tokens_computed")
state = (
computed,
total,
self._delta(current, "remote_hits"),
self._delta(current, "tokens_reused"),
)
if not total or state == self._last:
return
self._last = state
percent = min(100.0, 100.0 * computed / total)
print(
f"[autoresearch] Strategy Prefill: {computed}/{total} tokens "
f"({percent:.1f}%) · remote_hits={state[2]} reused={state[3]}",
flush=True,
)


def propose_candidate(
*,
address: str,
Expand Down Expand Up @@ -175,15 +245,29 @@ def propose_candidate(
enable_thinking=False,
)
generated: list[int] = []
print(
f"[autoresearch] Strategy Prefill: 0/{len(ids)} tokens (0.0%)",
flush=True,
)
with Client(address) as client:
with client.create_session(
eos_token_ids=_resolve_eos_token_ids(tokenizer),
client_label="autoresearch-strategy",
) as session:
session.append(ids)
with StrategyPrefillHeartbeat():
session.append(ids)
print(
f"[autoresearch] Strategy Prefill complete: {len(ids)} tokens",
flush=True,
)
while len(generated) < 2048:
before = len(generated)
generated.extend(int(token) for token in session.generate(max_tokens=64))
print(
f"[autoresearch] Strategy Decode: {len(generated)} tokens "
f"stop_reason={session.last_stop_reason}",
flush=True,
)
if session.last_stop_reason != 1:
break
if len(generated) == before:
Expand Down
24 changes: 24 additions & 0 deletions tests/inference_engine/bench/test_autoresearch_supervisor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
read_results,
render_candidate,
should_keep,
StrategyPrefillHeartbeat,
validate_candidate,
)
from pathlib import Path
Expand Down Expand Up @@ -164,6 +165,29 @@ def fake_run(command, **kwargs):
assert "a[i+1]='128'" in remote


def test_strategy_prefill_heartbeat_reports_delta(monkeypatch, capsys):
heartbeat = StrategyPrefillHeartbeat(interval_s=0.01)
heartbeat._baseline = {
"remote_job_tokens_total": 100,
"remote_job_tokens_computed": 100,
"remote_hits": 2,
"tokens_reused": 20,
}
monkeypatch.setattr(
"autoresearch.prefill.supervisor._json_request",
lambda _url: {"prefill": {
"remote_job_tokens_total": 300,
"remote_job_tokens_computed": 228,
"remote_hits": 3,
"tokens_reused": 84,
}},
)
heartbeat._emit()
output = capsys.readouterr().out
assert "128/200 tokens (64.0%)" in output
assert "remote_hits=1 reused=64" in output


def test_supervisor_predeploys_before_real_strategy_proposal():
source = (
Path(__file__).resolve().parents[3]
Expand Down
Loading