159 lines
5.6 KiB
Python
159 lines
5.6 KiB
Python
"""Small, dependency-free helpers shared by the P0 vLLM extensions."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import threading
|
|
import time
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
_LOCK = threading.Lock()
|
|
_POLICY: dict[str, Any] | None = None
|
|
|
|
|
|
def _canonical(value: Any) -> str:
|
|
return json.dumps(value, ensure_ascii=True, separators=(",", ":"), sort_keys=True)
|
|
|
|
|
|
def digest(value: Any) -> str:
|
|
return hashlib.blake2b(_canonical(value).encode("utf-8"), digest_size=16).hexdigest()
|
|
|
|
|
|
def policy() -> dict[str, Any]:
|
|
global _POLICY
|
|
if _POLICY is not None:
|
|
return _POLICY
|
|
path_value = os.environ.get("COLLECTIVESPEC_P0_POLICY_PATH")
|
|
if not path_value:
|
|
raise RuntimeError("COLLECTIVESPEC_P0_POLICY_PATH is required for P0")
|
|
path = Path(path_value)
|
|
value = json.loads(path.read_text(encoding="utf-8"))
|
|
if not isinstance(value, dict):
|
|
raise RuntimeError("P0 policy must be a JSON object")
|
|
max_k = value.get("max_k")
|
|
seed = value.get("seed")
|
|
kind = value.get("policy")
|
|
if isinstance(max_k, bool) or not isinstance(max_k, int) or max_k < 0:
|
|
raise RuntimeError("P0 policy max_k must be a non-negative integer")
|
|
if isinstance(seed, bool) or not isinstance(seed, int):
|
|
raise RuntimeError("P0 policy seed must be an integer")
|
|
if kind not in {"blake2b-request-static-k", "constant-k"}:
|
|
raise RuntimeError(f"Unsupported P0 policy: {kind!r}")
|
|
if kind == "constant-k":
|
|
constant_k = value.get("constant_k")
|
|
if isinstance(constant_k, bool) or not isinstance(constant_k, int):
|
|
raise RuntimeError("constant-k policy requires integer constant_k")
|
|
if not 0 <= constant_k <= max_k:
|
|
raise RuntimeError("constant_k must be within [0, max_k]")
|
|
_POLICY = value
|
|
return value
|
|
|
|
|
|
def requested_k(request_id: str, candidate_count: int) -> int:
|
|
value = policy()
|
|
max_k = int(value["max_k"])
|
|
if candidate_count < 0:
|
|
raise RuntimeError("candidate_count must be non-negative")
|
|
if value["policy"] == "constant-k":
|
|
chosen = int(value["constant_k"])
|
|
else:
|
|
material = f"{value['seed']}|{request_id}".encode("utf-8")
|
|
chosen = int.from_bytes(hashlib.blake2b(material, digest_size=8).digest(), "big") % (max_k + 1)
|
|
return min(chosen, candidate_count)
|
|
|
|
|
|
def histogram(values: list[int]) -> dict[str, int]:
|
|
counts: dict[str, int] = {}
|
|
for value in values:
|
|
key = str(int(value))
|
|
counts[key] = counts.get(key, 0) + 1
|
|
return dict(sorted(counts.items(), key=lambda item: int(item[0])))
|
|
|
|
|
|
def _jsonable(value: Any) -> Any:
|
|
if value is None or isinstance(value, (str, int, float, bool)):
|
|
return value
|
|
if isinstance(value, (list, tuple)):
|
|
return [_jsonable(item) for item in value]
|
|
if isinstance(value, dict):
|
|
return {str(key): _jsonable(item) for key, item in value.items()}
|
|
tolist = getattr(value, "tolist", None)
|
|
if callable(tolist):
|
|
return _jsonable(tolist())
|
|
item = getattr(value, "item", None)
|
|
if callable(item):
|
|
try:
|
|
return _jsonable(item())
|
|
except (TypeError, ValueError):
|
|
pass
|
|
return repr(value)
|
|
|
|
|
|
def dp_metadata_summary(metadata: Any) -> dict[str, Any] | None:
|
|
if metadata is None:
|
|
return None
|
|
keys = (
|
|
"num_pad_tokens",
|
|
"num_tokens_across_dp",
|
|
"num_tokens_per_rank",
|
|
"num_reqs_per_rank",
|
|
"is_prompt_batch",
|
|
"all_prefill",
|
|
"all_decode",
|
|
"should_ubatch",
|
|
"input_fits_in_drafter",
|
|
"cudagraph_mode",
|
|
)
|
|
return {key: _jsonable(getattr(metadata, key, None)) for key in keys}
|
|
|
|
|
|
def plan_summary(scheduler_output: Any) -> dict[str, Any]:
|
|
scheduled = getattr(scheduler_output, "num_scheduled_tokens", {}) or {}
|
|
spec = getattr(scheduler_output, "scheduled_spec_decode_tokens", {}) or {}
|
|
ordered = [
|
|
{
|
|
"request_id": str(request_id),
|
|
"scheduled_tokens": int(tokens),
|
|
"spec_k": len(spec.get(request_id, [])),
|
|
}
|
|
for request_id, tokens in scheduled.items()
|
|
]
|
|
semantic = sorted(ordered, key=lambda item: item["request_id"])
|
|
spec_ks = [item["spec_k"] for item in ordered if item["spec_k"] > 0]
|
|
return {
|
|
"ordered_plan_digest": digest(ordered),
|
|
"semantic_plan_digest": digest(semantic),
|
|
"request_count": len(ordered),
|
|
"total_scheduled_rows": int(getattr(scheduler_output, "total_num_scheduled_tokens", 0)),
|
|
"spec_request_count": len(spec),
|
|
"spec_k_histogram": histogram(spec_ks),
|
|
"verify_logical_rows": sum(1 + item["spec_k"] for item in ordered if item["spec_k"] > 0),
|
|
"scheduled_dp_metadata": dp_metadata_summary(
|
|
getattr(scheduler_output, "scheduled_dp_metadata", None)
|
|
),
|
|
}
|
|
|
|
|
|
def log_event(role: str, event: str, **payload: Any) -> None:
|
|
root_value = os.environ.get("COLLECTIVESPEC_P0_LOG_DIR")
|
|
if not root_value:
|
|
raise RuntimeError("COLLECTIVESPEC_P0_LOG_DIR is required for P0")
|
|
root = Path(root_value)
|
|
root.mkdir(parents=True, exist_ok=True)
|
|
record = {
|
|
"schema_version": "collectivespec-p0-log-v1",
|
|
"event": event,
|
|
"monotonic_s": time.monotonic(),
|
|
"pid": os.getpid(),
|
|
"policy_version": policy().get("version"),
|
|
"role": role,
|
|
**{key: _jsonable(value) for key, value in payload.items()},
|
|
}
|
|
path = root / f"{role}-pid{os.getpid()}.jsonl"
|
|
with _LOCK, path.open("a", encoding="utf-8") as handle:
|
|
handle.write(json.dumps(record, ensure_ascii=True, sort_keys=True) + "\n")
|