Download arctag/__init__.py from WhissleAI/arc-tagger: direct link, hf CLI and curl.
- Browser
- Download file 6.44 kB
-
https://huggingface.co/WhissleAI/arc-tagger/resolve/main/arctag/__init__.py
- Command line
-
hf download hf://WhissleAI/arc-tagger/arctag/__init__.py
-
curl -L -o __init__.py https://huggingface.co/WhissleAI/arc-tagger/resolve/main/arctag/__init__.py
6.44 kB
| """Arc Tagger: text-only streaming tagger (about 10 M parameters, CPU) for ASR word streams. | |
| from arctag import ArcTagger | |
| tg = ArcTagger.from_pretrained() # WhissleAI/arc-tagger from the Hub, or a local dir | |
| out = tg.tag("Hi, this is Dana from Acme. Can you move my appointment to Friday?") | |
| st = tg.stream(domain="talk") # streaming: words in as the ASR emits them | |
| st.push_words(["hi", "this", "is"], final=False) # interim | |
| st.push_words(["hi,", "this", "is", "Dana."], revise_from=0, final=True) | |
| st.endpoint() | |
| res = st.finalize() | |
| CPU only. The model code is the streaming engine in arctag/_core. | |
| """ | |
| import importlib.util, json, os, sys | |
| __version__ = "0.4.1" | |
| _HERE = os.path.dirname(os.path.abspath(__file__)) | |
| _CORE = os.path.join(_HERE, "_core") | |
| _EN = None | |
| def _engine(home): | |
| """import the vendored engine once (module-level path constants resolve against `home`)""" | |
| global _EN | |
| if _EN is None: | |
| os.environ.setdefault("CUDA_VISIBLE_DEVICES", "") | |
| os.environ.setdefault("ARCTAG_HOME", home) | |
| os.environ["ARCTAG_ROLE"] = "offline" | |
| if _CORE not in sys.path: | |
| sys.path.insert(0, _CORE) | |
| spec = importlib.util.spec_from_file_location("arctag_engine", os.path.join(_CORE, "engine.py")) | |
| _EN = importlib.util.module_from_spec(spec); sys.modules["arctag_engine"] = _EN | |
| spec.loader.exec_module(_EN) | |
| return _EN | |
| def _driver(): | |
| from . import driver | |
| return driver | |
| class ArcTagger: | |
| def __init__(self, home, EN, loaded, cfg): | |
| self.home, self.EN, self.loaded, self.config = home, EN, loaded, cfg | |
| def from_pretrained(cls, path="WhissleAI/arc-tagger", revision=None, thresholds="refit"): | |
| """path: release dir (model.safetensors, config.json, tok/, ontology/, thresholds/) or a Hugging Face repo id. | |
| thresholds: "refit" (default: thresholds/display.json, plus thresholds/events.json and span_head.json when present) | |
| or "none" (the default thresholds in config.json only; engine display floors still apply).""" | |
| if not os.path.isdir(path): | |
| from huggingface_hub import snapshot_download | |
| path = snapshot_download(path, revision=revision, allow_patterns=["*.json", "*.safetensors", "*.npy", "*.py", "*.txt", "*.md", | |
| "LICENSE*", "tok/*", "ontology/*", "thresholds/*", "arctag/*"]) | |
| home = os.path.abspath(path) | |
| EN = _engine(home) | |
| AT = sys.modules["arc_text"]; TT = sys.modules["tagtable"] | |
| AT.BPE8K = os.path.join(home, "tok", "bpe8k", "tokenizer.json") | |
| cfg = json.load(open(os.path.join(home, "config.json"))) | |
| ext = os.path.join(home, cfg.get("onto_ext") or "ontology/ext_v7_aff.json") | |
| T = TT.TextTable(r8=os.path.join(home, "ontology", "tags_v1_r8.json"), | |
| text_tags=os.path.join(home, "ontology", "text_tags.json"), ext=ext) | |
| T.load_E(os.path.join(home, "ontology", "head_E_r8_bge_small.npy")) | |
| if T.sha != cfg.get("ontology_sha"): | |
| raise RuntimeError(f"ontology sha {T.sha} != config {cfg.get('ontology_sha')}") | |
| loaded = _load_model(EN, home, cfg, T, thresholds) | |
| return cls(home, EN, loaded, cfg) | |
| def stream(self, domain="auto", tier="T0"): | |
| """a streaming session (driver.Stream): push_words(words, final=True, revise_from=None, endpoint=False), endpoint(), | |
| finalize() -> result dict""" | |
| return _driver().Stream(self.EN, self.loaded, domain=domain, tier=tier, meta=self._meta()) | |
| def tag(self, x, domain="auto", tier="T0"): | |
| """x: text (str), a list of words (str) or word dicts {"w", "t0", "t1", "conf", "spk", "final"}. Utterances end after | |
| words ending in . ? ! Returns the structured result (see README "Output").""" | |
| return _driver().tag(self.EN, self.loaded, x, domain=domain, tier=tier, meta=self._meta()) | |
| def _meta(self): | |
| return {"model": "arc-tagger", "version": __version__} | |
| def _read_thr(display, events): | |
| """thresholds/display.json {"thresholds": {tag@readout: {domain|_all: thr}}} and thresholds/events.json {"tier", "hide", | |
| "thresholds"}""" | |
| pref, pstep, ev, evstep = {}, None, {}, None | |
| if os.path.exists(display): | |
| pref, pstep = json.load(open(display)).get("thresholds") or {}, "display" | |
| if os.path.exists(events): | |
| ev, evstep = json.load(open(events)), "events" | |
| return pref, pstep, ev, evstep | |
| def _load_model(EN, home, cfg, T, thresholds="refit"): | |
| """load model.safetensors and the threshold tables of a release dir""" | |
| from safetensors.torch import load_file | |
| AT = sys.modules["arc_text"]; SA = sys.modules["stream_arc"] | |
| step = None | |
| m = AT.ArcText(T, scratch=cfg.get("scratch") or AT.SCRATCH_10M) | |
| sd = load_file(os.path.join(home, "model.safetensors")) | |
| SA.add_modules_for(m, sd) | |
| missing, unexpected = m.load_state_dict(sd, strict=False) | |
| if unexpected: | |
| raise RuntimeError(f"weights without a module: {unexpected[:6]}") | |
| bad = [k for k in missing if not k.startswith(("head.E",))] | |
| if bad: | |
| import warnings | |
| warnings.warn(f"{len(bad)} missing keys (e.g. {bad[:3]})") | |
| m.eval() | |
| own = cfg.get("thresholds") or {} | |
| pref, pstep, ev, evstep = {}, None, {}, None | |
| if thresholds == "refit": | |
| d = os.path.join(home, "thresholds") | |
| pref, pstep, ev, evstep = _read_thr(os.path.join(d, "display.json"), os.path.join(d, "events.json")) | |
| sh = os.path.join(d, "span_head.json") # span-head thresholds per tier | |
| if os.path.exists(sh): | |
| ts = {t: dict(v) for t, v in (cfg.get("thresholds_span") or {}).items()} | |
| for t, v in (json.load(open(sh)).get("thresholds_span") or {}).items(): | |
| ts.setdefault(t, {}).update(v) | |
| cfg = dict(cfg, thresholds_span=ts) | |
| elif thresholds != "none": | |
| raise ValueError("thresholds must be 'refit' or 'none'") | |
| thr = EN.Thresholds(own, {}, None, pref=pref, pref_step=pstep) | |
| thr.set_events(ev, evstep) | |
| L = EN.Loaded(step, home, m, T, thr, cfg) | |
| fams = cfg.get("span_head_families") # span families decoded from the span head (others: typed span decode) | |
| if fams is not None and L.head_tags: | |
| L.head_use, L.head_why = set(fams) & set(L.head_tags), "config" | |
| return L | |