PyTorch
Safetensors
action-model
function-calling
tool-calling
tool-use
non-autoregressive
masked-diffusion
modernbert
shing-dev's picture
ActionMan-0.1 research preview
6fd3740 verified
Raw History Blame Contribute Delete
2.95 kB
"""Public API: load a released ActionMan and turn (query, tools) into tool calls.
from actionman.api import ActionMan
am = ActionMan.from_pretrained("shing-dev/ActionMan-0.1") # or a local export directory
am.predict("Weather in Paris in celsius?", tools) # tools: OpenAI-style function schemas
# -> [{"name": "get_weather", "arguments": {"city": "Paris", "unit": "celsius"}, "confidence": 0.97}]
"""
from __future__ import annotations
import json
import os
import torch
from . import cached as C
from .features import load_tokenizer
from .infer import predict as _predict
from .model import ActionMan as _Model
from .schema import Example, from_openai_tool
class ActionMan:
def __init__(self, model, tok, flags: dict, device: str):
self.model, self.tok, self.flags, self.device = model, tok, flags, device
@classmethod
def from_pretrained(cls, path: str, device: str | None = None, compile: bool | None = None, token: str | None = None):
"""path: a local export dir or a Hugging Face repo id. compile (default: on GPU): padded static encoders
under torch.compile, about 4x faster per call after a one-off warm-up."""
from safetensors.torch import load_file
from transformers import ModernBertConfig
if not os.path.isdir(path):
from huggingface_hub import snapshot_download
path = snapshot_download(path, token=token)
cfg = json.load(open(os.path.join(path, "config.json")))
device = device or ("cuda" if torch.cuda.is_available() else "cpu")
tok = load_tokenizer(path)
model = _Model.from_encoder(ModernBertConfig.from_dict(cfg["encoder"]), tok, v3=cfg["flags"]["v3"])
model.load_state_dict({k: v.float() for k, v in load_file(os.path.join(path, "model.safetensors")).items()})
model = model.to(device).eval()
if compile if compile is not None else device == "cuda": # process-wide swap, as in evaluate --compile
C.encode_context, C.encode_slots = C.make_static(model.mlm.model)
return cls(model, tok, cfg["flags"], device)
@torch.no_grad()
def predict(self, query: str, tools: list[dict], T: int = 3, require: bool = False) -> list[dict]:
"""Tool calls for `query`, best first. Empty list = no tool fits (unless require=True, like tool_choice="required").
confidence = min(tool confidence, every argument's confidence). It ranks errors well enough to abstain on
(docs/results/benchmark.md: the least confident 10% abstaining takes our eval from 78.6 to 85.1)."""
ex = Example(query, [from_openai_tool(t) for t in tools], [])
pred = _predict(self.model, self.tok, ex, T=T, device=self.device, require=require, **self.flags)
return [{"name": c.name, "arguments": c.args,
"confidence": round(min([c.conf_tool, *c.conf_args.values()]), 4)} for c in pred.calls]