Download code/actionman/infer.py from shing-dev/ActionMan-0.1: direct link, hf CLI and curl.
- Browser
- Download file 5.38 kB
-
https://huggingface.co/shing-dev/ActionMan-0.1/resolve/main/code/actionman/infer.py
- Command line
-
hf download hf://shing-dev/ActionMan-0.1/code/actionman/infer.py
-
curl -L -o infer.py https://huggingface.co/shing-dev/ActionMan-0.1/resolve/main/code/actionman/infer.py
5.38 kB
| """Inference: pass A (decide tools + arg lengths) -> slot block -> mask-predict refinement.""" | |
| from __future__ import annotations | |
| from dataclasses import dataclass, field | |
| import torch | |
| from .features import BUCKETS, MAX_CALLS, OVERFLOW, SPECIAL, build_pass_a, build_pass_b | |
| from .schema import Call, Example, text_to_value | |
| class PredCall: | |
| name: str | |
| args: dict = field(default_factory=dict) | |
| conf_tool: float = 0.0 | |
| conf_args: dict = field(default_factory=dict) | |
| escalated: list = field(default_factory=list) | |
| def as_call(self) -> Call: | |
| return Call(self.name, self.args) | |
| class Prediction: | |
| calls: list[PredCall] | |
| passes: int = 0 | |
| skipped: bool = False | |
| def _banned_ids(tok, vocab: int) -> torch.Tensor: | |
| ids = [tok.mask_token_id, tok.cls_token_id, tok.sep_token_id] + [tok.am[s] for s in SPECIAL] | |
| ids += list(range(len(tok), vocab)) | |
| return torch.tensor(sorted(set(i for i in ids if i < vocab))) | |
| def predict(model, tok, ex: Example, T: int = 2, tau: float = 0.85, device="cpu", cached: bool = False, | |
| markers: bool = False, v3: bool = False, snap: bool = False, require: bool = False) -> Prediction: | |
| if v3: | |
| from .infer_v3 import predict_v3 | |
| return predict_v3(model, tok, ex, T=T, tau=tau, device=device, cached=cached, markers=markers, snap=snap, require=require) | |
| model.eval() | |
| ex0 = Example(ex.query, ex.tools, [], ex.id, ex.source) | |
| a = build_pass_a(tok, ex0) | |
| if a is None or not ex.tools: | |
| return Prediction([], 0, skipped=a is None) | |
| ids = torch.tensor([a["input_ids"]], device=device) | |
| if cached: | |
| from .cached import encode_context, encode_slots | |
| h, kv = encode_context(model.mlm.model, ids) | |
| else: | |
| h = model.encode(ids, torch.ones_like(ids)) | |
| J = len(a["t_pos"]) | |
| call_pos = torch.tensor([a["call_pos"]], device=device) | |
| t_pos = torch.tensor([a["t_pos"]], device=device) | |
| probs = model.decision_logits(h, call_pos, t_pos, torch.ones_like(t_pos, dtype=torch.bool)).float().softmax(-1)[0] | |
| chosen = [] # (call_i, tool_j, conf) | |
| for i in range(MAX_CALLS): | |
| j = int(probs[i].argmax()) | |
| if j == J: | |
| break | |
| chosen.append((i, j, float(probs[i, j]))) | |
| if not chosen: | |
| return Prediction([], 1) | |
| rows, keys = [], [] | |
| for i, j, _ in chosen: | |
| for k in range(len(ex.tools[j].params)): | |
| rows.append((0, a["call_pos"][i], a["arg_pos"][(j, k)])) | |
| keys.append((i, j, k)) | |
| len_logits = model.length_logits(h, torch.tensor(rows, device=device).reshape(-1, 3)).float() | |
| plan, preds = [], [] | |
| for ci, (i, j, conf) in enumerate(chosen): | |
| buckets, pc = {}, PredCall(ex.tools[j].name, conf_tool=conf) | |
| for r, (ri, rj, k) in enumerate(keys): | |
| if ri != i: | |
| continue | |
| lg = len_logits[r].clone() | |
| if not ex.tools[j].params[k].optional: | |
| lg[0] = float("-inf") # required params can't be absent | |
| b = int(lg.argmax()) | |
| buckets[k] = b | |
| if b == OVERFLOW: | |
| pc.escalated.append(ex.tools[j].params[k].name) | |
| plan.append((j, buckets)) | |
| preds.append(pc) | |
| b = build_pass_b(tok, ex0, a, plan, with_targets=False, keep_context=cached, markers=markers) | |
| passes = 1 | |
| if b is not None and b["slot_pos"]: | |
| ids_b = torch.tensor([b["input_ids"]], device=device) | |
| flat = [p for pos in b["slot_pos"] for p in pos] | |
| fpos = torch.tensor(flat, device=device) | |
| conf = torch.zeros(len(flat), device=device) | |
| masked = torch.ones(len(flat), dtype=torch.bool, device=device) | |
| vocab = model.mlm.decoder.weight.shape[0] | |
| banned = _banned_ids(tok, vocab).to(device) | |
| for t in range(T): | |
| passes = t + 1 | |
| if cached: | |
| c0 = b["ctx_len"] | |
| hs = encode_slots(model.mlm.model, ids_b[:, c0:], c0, kv)[0, fpos - c0] | |
| logits = model.mlm.decoder(model.mlm.head(hs)).float() | |
| else: | |
| logits = model.forward_b(ids_b, torch.ones_like(ids_b), torch.zeros_like(fpos), fpos).float() | |
| logits[:, banned] = float("-inf") | |
| p, tokens = logits.softmax(-1).max(-1) | |
| conf = torch.where(masked, p, conf) | |
| ids_b[0, fpos[masked]] = tokens[masked] | |
| if t == T - 1: | |
| break | |
| masked = conf < tau | |
| if not masked.any(): | |
| break | |
| ids_b[0, fpos[masked]] = tok.mask_token_id | |
| out_tokens = ids_b[0, fpos].tolist() | |
| confs = conf.tolist() | |
| cursor = 0 | |
| for (i, k), pos in zip(b["slot_index"], b["slot_pos"]): | |
| n = len(pos) | |
| toks, cs = out_tokens[cursor: cursor + n], confs[cursor: cursor + n] | |
| cursor += n | |
| if tok.pad_token_id in toks: | |
| toks = toks[: toks.index(tok.pad_token_id)] | |
| text = tok.decode(toks, skip_special_tokens=True).strip() | |
| ci = [c[0] for c in chosen].index(i) | |
| param = ex.tools[chosen[ci][1]].params[k] | |
| preds[ci].args[param.name] = text_to_value(text, param.type) | |
| preds[ci].conf_args[param.name] = float(min(cs)) if cs else 1.0 | |
| return Prediction(preds, passes + 1) | |