"""NER layer package for Module 8 resume parsing. Layers (merge-all semantics — see research/06-dataset-sourcing.md §5 and the plan at ~/.claude/plans/okay-plan-for-7a-fluffy-marshmallow.md): 1. nucha — Nucha/Nucha_ITSkillNER_BERT (MIT, F1 0.90) 2. jobbert — jjzha/jobbert_skill_extraction (license unspecified, FYP-OK) 3. skillner — rule-based, EMSI taxonomy (Nov 2021 release, still works) 4. sbert — SentenceTransformer('all-MiniLM-L6-v2') + pgvector cosine Each concrete layer implements ``NerLayer`` and exposes a module-level lazy singleton so the HF models only load on first call. The dispatcher in ``resume_parser._predict_skills_from_text`` invokes the layers in order, catches any import / timeout / model-download error per layer, and unions the results into one {alias: max_confidence} dict. The lexical floor (``ner.lexical``) is always run last even if the higher layers succeed — it guarantees the endpoint returns at least the catalog matches regardless of ML-layer availability. """ from __future__ import annotations from abc import ABC, abstractmethod class NerLayer(ABC): """Contract every NER layer must satisfy. Implementations should: * Keep the model as a module-level singleton, loaded on first call. * Catch *internal* model errors and return an empty dict rather than raising — the dispatcher already wraps calls in try/except, but returning {} for recoverable failures keeps the log quieter. * Never raise for an empty or non-English input — just return {}. """ #: Short stable identifier used in the response's ``parser_version`` list. #: Keep lowercase, no spaces. name: str = "abstract" @abstractmethod def predict(self, text: str) -> dict[str, float]: """Return {skill_or_alias_name: confidence_0_to_1}. Keys are free-text — the dispatcher runs alias resolution against the Skill catalog before they reach the user. Values are [0, 1] floats. """ raise NotImplementedError def available(self) -> bool: """Cheap check the dispatcher calls before invoking predict(). Default: attempt a lazy-import of the layer's heavy deps. Override for layers whose availability depends on external state (e.g. a model being cached locally). """ return True