ManniX-ITA commited on
Commit
4e8fde4
·
verified ·
1 Parent(s): 565f873

package_courier: fix sampler (tps_ewma field, run-lifetime sampling, w-mode) + report (x-domain clipping, t0 anchor, aggregate-tps/max-ctx tiles) — bug-2223

Browse files
bench/package_courier/agent_app.py CHANGED
@@ -160,6 +160,7 @@ class Runtime:
160
  self.target = cfg.workers
161
  self.live = 0 # threads actually running; idx is monotonic, never a count
162
  self.no_more_packages = threading.Event()
 
163
  self.events: list[dict] = []
164
  self.workers: list[Worker] = []
165
 
@@ -383,18 +384,26 @@ def engine_props(url: str) -> dict:
383
 
384
 
385
  def sampler(rt: Runtime, out_path: Path):
 
 
 
386
  http = httpx.Client(base_url=rt.cfg.engine, timeout=10)
387
- with out_path.open("a") as f:
388
- while not rt.no_more_packages.is_set():
389
  row = {"t": round(time.time(), 3)}
390
  try:
391
  r = http.get("/polykv/tps", params={"once": "1"})
392
  if r.status_code == 200:
393
  d = r.json()
394
  row["mean_active_tps"] = d.get("mean_active_tps")
 
 
395
  row["sessions"] = [
396
- {k: s.get(k) for k in
397
- ("session_id", "tps", "ctx_used", "ctx_headroom_tokens")}
 
 
 
398
  for s in d.get("sessions", [])]
399
  except Exception:
400
  row["err"] = True
@@ -490,6 +499,7 @@ def main():
490
  rt.spawn_worker(schemas)
491
  for w in rt.workers:
492
  w.join()
 
493
 
494
  work = WorkClient(cfg.work)
495
  work.signal_done()
 
160
  self.target = cfg.workers
161
  self.live = 0 # threads actually running; idx is monotonic, never a count
162
  self.no_more_packages = threading.Event()
163
+ self.run_done = threading.Event() # set after the last worker joins
164
  self.events: list[dict] = []
165
  self.workers: list[Worker] = []
166
 
 
384
 
385
 
386
  def sampler(rt: Runtime, out_path: Path):
387
+ # "w" not "a": a relaunch into the same --out dir must not keep stale
388
+ # samples from a crashed attempt. Runs until the last worker exits
389
+ # (run_done) — the queue empties while episodes are still decoding.
390
  http = httpx.Client(base_url=rt.cfg.engine, timeout=10)
391
+ with out_path.open("w") as f:
392
+ while not rt.run_done.is_set():
393
  row = {"t": round(time.time(), 3)}
394
  try:
395
  r = http.get("/polykv/tps", params={"once": "1"})
396
  if r.status_code == 200:
397
  d = r.json()
398
  row["mean_active_tps"] = d.get("mean_active_tps")
399
+ # engine field is tps_ewma (recorded here as "tps"); keep
400
+ # "active" so idle sessions' stale EWMA isn't aggregated
401
  row["sessions"] = [
402
+ {"session_id": s.get("session_id"),
403
+ "tps": s.get("tps_ewma"),
404
+ "active": s.get("active"),
405
+ "ctx_used": s.get("ctx_used"),
406
+ "ctx_headroom_tokens": s.get("ctx_headroom_tokens")}
407
  for s in d.get("sessions", [])]
408
  except Exception:
409
  row["err"] = True
 
499
  rt.spawn_worker(schemas)
500
  for w in rt.workers:
501
  w.join()
502
+ rt.run_done.set()
503
 
504
  work = WorkClient(cfg.work)
505
  work.signal_done()
bench/package_courier/report_html.py CHANGED
@@ -24,6 +24,11 @@ def svg_chart(series: dict[str, list[tuple[float, float]]], title: str,
24
  step_series: set[str] | None = None) -> str:
25
  pad_l, pad_r, pad_t, pad_b = 56, 16, 30, 40
26
  step_series = step_series or set()
 
 
 
 
 
27
  pts = [p for s in series.values() for p in s]
28
  if not pts:
29
  return f"<p>{title}: no data</p>"
@@ -132,7 +137,8 @@ def build(run_dir: Path) -> Path:
132
  x_domain=xdom, step_series={"delivered"}))
133
 
134
  charts.append(svg_chart(
135
- {"delivery time": [(t, d) for t, d in episodes if d is not None]},
 
136
  "Delivery time per package (does the run slow down?)",
137
  "completion run time (s)", "episode duration (s)", x_domain=xdom))
138
 
@@ -157,6 +163,31 @@ def build(run_dir: Path) -> Path:
157
  x_domain=xdom, step_series={"worker target", "live workers"}))
158
 
159
  dt = rep.get("delivery_time_s") or {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  tiles = [
161
  ("Score", f"{rep.get('score_pct')}%"),
162
  ("Paid", f"${rep.get('paid_usd')}"),
@@ -168,6 +199,9 @@ def build(run_dir: Path) -> Path:
168
  ("Delivery mean / p90",
169
  " / ".join(f"{dt.get(k):.1f}s" if dt.get(k) is not None else "—"
170
  for k in ("mean", "p90"))),
 
 
 
171
  ("Tool errors", stats.get("tool_errors")),
172
  ("Workers", f"{stats.get('workers_initial', '?')} → {stats.get('workers_final')}"),
173
  ("tps floor", stats.get("tps_floor")),
 
24
  step_series: set[str] | None = None) -> str:
25
  pad_l, pad_r, pad_t, pad_b = 56, 16, 30, 40
26
  step_series = step_series or set()
27
+ if x_domain:
28
+ # clip to the shared clock — SVG polylines happily render outside the
29
+ # plot area, so stray points (e.g. stale samples) would smear the chart
30
+ series = {n: [p for p in s if x_domain[0] <= p[0] <= x_domain[1]]
31
+ for n, s in series.items()}
32
  pts = [p for s in series.values() for p in s]
33
  if not pts:
34
  return f"<p>{title}: no data</p>"
 
137
  x_domain=xdom, step_series={"delivered"}))
138
 
139
  charts.append(svg_chart(
140
+ {"delivery time": [(0.0, 0.0)]
141
+ + [(t, d) for t, d in episodes if d is not None]},
142
  "Delivery time per package (does the run slow down?)",
143
  "completion run time (s)", "episode duration (s)", x_domain=xdom))
144
 
 
163
  x_domain=xdom, step_series={"worker target", "live workers"}))
164
 
165
  dt = rep.get("delivery_time_s") or {}
166
+ # aggregate engine throughput per sample = sum of ACTIVE sessions' gen tps;
167
+ # window to the run clock so stale samples from a reused --out dir don't count
168
+ win_rows = [r for r in tps_rows if t0 <= r["t"] <= t_end]
169
+ agg = [sum(s.get("tps") or 0.0 for s in (r.get("sessions") or [])
170
+ if s.get("active", True))
171
+ for r in win_rows]
172
+ agg = [a for a in agg if a > 0]
173
+ if not agg:
174
+ # older runs sampled the wrong field (tps always null) — reconstruct
175
+ # from turn events: each turn generated out_toks at gen_tps, so it
176
+ # occupied [t - out/gen, t]; aggregate at time g = Σ gen_tps overlapping
177
+ turns = [(e["t"] - t0 - e["out_toks"] / e["gen_tps"], e["t"] - t0, e["gen_tps"])
178
+ for e in events if e.get("ev") == "turn"
179
+ and (e.get("gen_tps") or 0) > 0 and (e.get("out_toks") or 0) > 0]
180
+ grid = [g * 5.0 for g in range(int(T / 5.0) + 1)]
181
+ agg = [a for g in grid
182
+ if (a := sum(tps for b, e, tps in turns if b <= g <= e)) > 0]
183
+ agg.sort()
184
+ agg_avg = sum(agg) / len(agg) if agg else None
185
+ agg_p90 = agg[int(0.9 * (len(agg) - 1))] if agg else None
186
+ max_ctx = max((s.get("ctx_used") or 0 for r in win_rows
187
+ for s in (r.get("sessions") or [])), default=0)
188
+ if not max_ctx:
189
+ max_ctx = max((e.get("in_toks") or 0 for e in events
190
+ if e.get("ev") == "turn"), default=0)
191
  tiles = [
192
  ("Score", f"{rep.get('score_pct')}%"),
193
  ("Paid", f"${rep.get('paid_usd')}"),
 
199
  ("Delivery mean / p90",
200
  " / ".join(f"{dt.get(k):.1f}s" if dt.get(k) is not None else "—"
201
  for k in ("mean", "p90"))),
202
+ ("Aggregate gen tps avg / p90",
203
+ f"{agg_avg:.1f} / {agg_p90:.1f}" if agg else "—"),
204
+ ("Max ctx / session", f"{max_ctx:,} tok" if max_ctx else "—"),
205
  ("Tool errors", stats.get("tool_errors")),
206
  ("Workers", f"{stats.get('workers_initial', '?')} → {stats.get('workers_final')}"),
207
  ("tps floor", stats.get("tps_floor")),