JEV-CPU / src /semif_phase1 /serial.py
Meanblock's picture
Add JEV-CPU: CPU port of SemIf + web UI
7845694 verified
Raw History Blame Contribute Delete
5.79 kB
"""Serialized decisions over one-state native prefix caches."""
from __future__ import annotations
import copy
import hashlib
import inspect
import json
import time
from .core import direct_messages, softmax
from .direct import PROMPT_VERSION, encode_prompt
def _state_prefix(tokenizer, state) -> list[int]:
row = {
"id": "prefix-only",
"state": state,
"question": "prefix boundary placeholder",
"options": [{"id": "yes", "description": "Yes"}, {"id": "no", "description": "No"}],
}
turns = direct_messages(row)
prompt = tokenizer.apply_chat_template(
turns, tokenize=False, add_generation_prompt=True, enable_thinking=False
)
payload = turns[-1]["content"]
evidence = json.dumps({"evidence": state}, ensure_ascii=False)[:-1]
if prompt.count(payload) != 1 or not payload.startswith(evidence):
raise ValueError("Cannot establish a deterministic evidence prefix")
text = prompt[: prompt.index(payload)] + evidence
return tokenizer.encode(text, add_special_tokens=False)[:-1]
def _cached_forward(model, inputs):
parameters = inspect.signature(model.forward).parameters
if "logits_to_keep" not in parameters and hasattr(model, "get_base_model"):
parameters = inspect.signature(model.get_base_model().forward).parameters
if "logits_to_keep" not in parameters:
raise RuntimeError("Model lacks selective last-position logits")
return model(**inputs, use_cache=True, return_dict=True, logits_to_keep=1)
class SerialPrefixScorer:
"""Cache the current state, then score independent copied suffix branches."""
def __init__(self, model, tokenizer, metadata: dict, max_tokens: int = 4096):
self.model = model
self.tokenizer = tokenizer
self.metadata = {**metadata, "serving_config": "native-state-prefix-cache-v1"}
self.max_tokens = max_tokens
self.device = next(model.parameters()).device
self.cache = None
self.state = None
self.prefix = None
def score(self, row: dict) -> dict:
import torch
started = time.perf_counter()
ids, slots, prompt_hash = encode_prompt(self.tokenizer, row, self.max_tokens)
hit = self.cache is not None and row["state"] == self.state
prefix = self.prefix if hit else _state_prefix(self.tokenizer, row["state"])
if not prefix or ids[: len(prefix)] != prefix or len(ids) <= len(prefix):
raise ValueError("State prefix does not match the full prompt")
sync = lambda: torch.cuda.synchronize(self.device) if self.device.type == "cuda" else None
prefill_seconds = 0.0
self.model.eval()
with torch.inference_mode():
if not hit:
self.cache = self.state = self.prefix = None
sync()
mark = time.perf_counter()
output = _cached_forward(
self.model,
{
"input_ids": torch.tensor([prefix], dtype=torch.long, device=self.device),
"attention_mask": torch.ones((1, len(prefix)), dtype=torch.long, device=self.device),
},
)
self.cache = output.past_key_values
del output
sync()
prefill_seconds = time.perf_counter() - mark
if self.cache is None or self.cache.get_seq_length() != len(prefix):
raise RuntimeError("Invalid native prefix cache")
self.state, self.prefix = row["state"], prefix
sync()
mark = time.perf_counter()
branch = copy.deepcopy(self.cache)
sync()
copy_seconds = time.perf_counter() - mark
inputs = {
"input_ids": torch.tensor([ids[len(prefix) :]], dtype=torch.long, device=self.device),
"attention_mask": torch.ones((1, len(ids)), dtype=torch.long, device=self.device),
"past_key_values": branch,
}
sync()
mark = time.perf_counter()
output = _cached_forward(self.model, inputs)
sync()
suffix_seconds = time.perf_counter() - mark
vocabulary = output.logits[0, -1, :].float()
selected_tensor = vocabulary[slots]
selected = selected_tensor.cpu().tolist()
result = {
"id": row["id"],
"option_ids": [option["id"] for option in row["options"]],
"probabilities": softmax(selected),
"option_logits": selected,
"answer_token_ids": slots,
"input_tokens": len(ids),
"prompt_sha256": prompt_hash,
"prompt_version": PROMPT_VERSION,
"model": self.metadata,
"readout": "native-state-prefix-cache-last-position",
"probability_status": "conditional option score; uncalibrated as decision confidence",
"cache_hit": hit,
"prefix_tokens": len(prefix),
"prefix_sha256": hashlib.sha256(json.dumps(prefix).encode()).hexdigest(),
"allowed_token_mass": float(
(selected_tensor.logsumexp(-1) - vocabulary.logsumexp(-1)).exp()
),
"full_vocab_argmax_id": int(vocabulary.argmax()),
"prefill_seconds": prefill_seconds,
"copy_seconds": copy_seconds,
"suffix_forward_seconds": suffix_seconds,
}
del output, branch
sync()
result["forward_seconds"] = prefill_seconds + suffix_seconds
result["total_seconds"] = time.perf_counter() - started
return result