fix(s31): close 1 review BLOCKER — EmbedderConfig registry + --embedder-config, never an import path
This commit is contained in:
parent
cabe05acf3
commit
b9dd91cdbe
4 changed files with 240 additions and 1 deletions
|
|
@ -40,6 +40,7 @@ os.environ.setdefault("OMP_NUM_THREADS", "1")
|
|||
os.environ.setdefault("MKL_NUM_THREADS", "1")
|
||||
|
||||
import numpy as np # noqa: E402 — must follow the thread pins above; see module docstring
|
||||
from pydantic import BaseModel, model_validator # noqa: E402 — same ordering constraint
|
||||
|
||||
if TYPE_CHECKING: # verdicts imports agent_framework — keep it out of the runtime import graph
|
||||
from portfolio_optimiser.verdicts import ProposalFeatures, Verdict
|
||||
|
|
@ -132,6 +133,63 @@ class FakeEmbedder:
|
|||
return np.ascontiguousarray(vec / norm, dtype="<f8")
|
||||
|
||||
|
||||
# --- Embedder config seam: a CLOSED registry, deliberately never an import path -----------------
|
||||
|
||||
|
||||
class EmbedderConfig(BaseModel):
|
||||
"""Declarative embedder choice. ``type`` membership is enforced by ``build_embedder`` (unknown
|
||||
type → ``ValueError``); the validator pins the per-type required fields.
|
||||
|
||||
Deliberately NOT an import path. A ``"my_pkg.mod:MyEmbedder"`` string would be arbitrary code
|
||||
execution at config-load time, and would hand a config file the ability to import a module that
|
||||
opens a socket — precisely the capability ``NotifierConfig`` refuses to let config grant
|
||||
("egress opt-in is a code-level factory kwarg, so config alone can never grant it"). It would
|
||||
also route around this module's no-network guard, which is an AST/``sys.modules`` check scoped
|
||||
to ``semretrieval.py`` and blind to a third module by construction.
|
||||
|
||||
A future networked embedder therefore enters as a NEW REGISTRY BRANCH plus a code-level
|
||||
capability kwarg — never as config."""
|
||||
|
||||
type: str
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _required_fields_by_type(self) -> EmbedderConfig:
|
||||
if not self.type:
|
||||
raise ValueError("embedder config requires a non-empty type")
|
||||
return self
|
||||
|
||||
|
||||
def build_embedder(config: EmbedderConfig) -> Embedder:
|
||||
"""Type-dispatch factory over a CLOSED vocabulary. Mirrors ``notify.build_notifier``.
|
||||
|
||||
Shipped vocabulary: ``"fake"`` → ``FakeEmbedder`` (a deterministic sha256 projection carrying
|
||||
no semantics). Anything else raises ``ValueError`` naming the seam, rather than attempting to
|
||||
resolve it — see ``EmbedderConfig`` for why resolution from config is refused outright."""
|
||||
if config.type == "fake":
|
||||
return FakeEmbedder()
|
||||
raise ValueError(
|
||||
f"unknown embedder type: {config.type!r} (known: 'fake'; a new embedder is added as a "
|
||||
"registry branch in semretrieval.build_embedder, never as an import path in config)"
|
||||
)
|
||||
|
||||
|
||||
def load_embedder_config(path: str | Path) -> EmbedderConfig:
|
||||
"""Fail-fast standalone loader for an embedder config (mirrors ``dimension.load_dimension``).
|
||||
|
||||
An embedder config is *authoritative startup input*, so loading is fail-fast: a missing file
|
||||
raises ``FileNotFoundError`` and malformed/invalid content raises ``pydantic.ValidationError``.
|
||||
Deliberate contrast to the tolerant verdict-inbox RAW layer (``load_verdicts_from_dir``), which
|
||||
skips bad files rather than raising — startup configuration must never be silently degraded.
|
||||
|
||||
:raises FileNotFoundError: ``path`` does not point at an existing file.
|
||||
:raises pydantic.ValidationError: the JSON is malformed or violates the schema.
|
||||
"""
|
||||
p = Path(path)
|
||||
if not p.is_file():
|
||||
raise FileNotFoundError(f"embedder config not found: {str(path)!r}")
|
||||
return EmbedderConfig.model_validate_json(p.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def cosine(a: np.ndarray, b: np.ndarray) -> float:
|
||||
"""Cosine similarity, with a zero-norm guard returning ``0.0``.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue