decode_stream.py 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637
  1. #!/usr/bin/env python3
  2. """Per-workload decode for glm53, measured the way the winning-config numbers were:
  3. thinking off, 512 output tokens, temp 0, streaming, drafter acceptance from /metrics.
  4. Median of 3. Usage: decode_stream.py
  5. """
  6. import json, os, statistics, sys, time, urllib.request
  7. BASE = os.environ.get("GATE_URL", "http://127.0.0.1:8002")
  8. MODEL = os.environ.get("GATE_MODEL", "glm53")
  9. def metrics():
  10. t = urllib.request.urlopen(BASE + "/metrics", timeout=10).read().decode()
  11. g = lambda k: sum(float(l.rsplit(" ", 1)[1]) for l in t.splitlines() if l.startswith(k + "{") or l.startswith(k + " "))
  12. return g("vllm:spec_decode_num_drafts_total"), g("vllm:spec_decode_num_accepted_tokens_total"), g("vllm:spec_decode_num_draft_tokens_total")
  13. def stream(prompt, n):
  14. body = {"model": MODEL, "max_tokens": n, "temperature": 0, "stream": True, "stream_options": {"include_usage": True},
  15. "chat_template_kwargs": {"enable_thinking": False}, "messages": [{"role": "user", "content": prompt}]}
  16. req = urllib.request.Request(BASE + "/v1/chat/completions", data=json.dumps(body).encode(), headers={"Content-Type": "application/json"})
  17. t0 = time.monotonic(); first = None; toks = 0
  18. with urllib.request.urlopen(req, timeout=900) as r:
  19. for line in r:
  20. line = line.decode().strip()
  21. if not line.startswith("data: ") or line == "data: [DONE]": continue
  22. d = json.loads(line[6:])
  23. if first is None and d.get("choices") and (d["choices"][0]["delta"].get("content") or d["choices"][0]["delta"].get("reasoning")):
  24. first = time.monotonic()
  25. if d.get("usage"): toks = d["usage"]["completion_tokens"]
  26. end = time.monotonic()
  27. return toks, toks / (end - first) if first and end > first else 0.0
  28. W = {"structured": "Count from 1 to 200, one number per line, digits only.",
  29. "code": "Write a complete Python implementation of a red-black tree with insert, delete and in-order traversal.",
  30. "prose": "Explain in detail how a hash map handles collisions, with examples."}
  31. stream("hi", 4)
  32. for name, p in W.items():
  33. rates, accs = [], []
  34. for _ in range(3):
  35. d0, a0, t0 = metrics(); n, r = stream(p, 512); d1, a1, t1 = metrics()
  36. rates.append(r); accs.append((a1 - a0) / (t1 - t0) if t1 > t0 else float("nan"))
  37. print(f"{name:10} decode {statistics.median(rates):6.1f} tok/s acceptance {100*statistics.median(accs):5.1f}% ({n} tok)", flush=True)