mixle.system.registry module

Registry – a local directory + index of fitted task models, queryable by capability and fingerprint.

The registry is the local library catalog that orchestrators, routers, capture flows, and accumulation workflows can read from or write into. Deliberately a directory plus a JSON index, not a server: every entry is a saved TaskModel, CalibratedTaskModel, or an IC-1 Posterior-conforming field-posterior artifact directory (see mixle.task.artifact for the first two; the third is written/read through mixle_pde.io.artifacts – IC-2 – lazily imported so this module never hard-depends on the mixle_pde plugin). A small index record names each entry’s capabilities, task fingerprint (task_fingerprint()), and capture profile. find_for answers “do I already have something for this task”; tier_stack turns a matching capability into an ascending-cost tier list – the shape Router consumes directly (Router(tiers=stack)), with the frontier appended last as the router’s own fallback tier.

class Registry(dir)[source]

Bases: object

A dir-backed catalog of registered models: register writes an artifact + index entry; find_for/tier_stack query it. Re-opening the same dir in a fresh process sees every entry.

Parameters:

dir (str)

register(model, *, capabilities, fingerprint=None, profile=None, cost=0.0, entry_id=None)[source]

Save model’s artifact under dir and add its index entry; return the entry.

model is a fitted TaskModel, a CalibratedTaskModel, or a mixle_pde field posterior (a “field_posterior” kind, IC-2) – the three artifact-saveable model kinds. capabilities names what this model answers (matched by find_for()); fingerprint is typically task_fingerprint()’s vector for the training data; profile is free-form (e.g. a capture_profile() dict); cost is the per-request cost used to order tier_stack(). An explicit entry_id that already exists (in the index or as an artifact directory) raises rather than duplicating the index row and silently overwriting the artifact; auto-generated ids scan past taken ones.

entry_id must be a single safe path component (_safe_entry_id()): an unvalidated value such as "../escaped" would otherwise write the artifact outside dir instead of inside it.

Every field of the index row is validated (by constructing the RegistryEntry) BEFORE the id is claimed or the artifact written, so a rejected argument leaves the registry byte-for-byte unchanged and the id free to retry – see the comment at that construction (MXR-080-1902).

Allocating an id and persisting the index is a read-modify-write over the on-disk index: two Registry instances opened on the same dir (or two threads/processes sharing one) each cache their own self._entries snapshot from construction time and otherwise never refresh it, so one instance’s register can silently overwrite another’s already-persisted index row when it writes back its own stale-plus-one view – even when the two calls pick different, non-colliding entry ids. Fixed by serializing id-allocation-through-index-write with an fcntl.flock on a lock file at the registry root, re-reading the on-disk index fresh under the lock rather than trusting the cached snapshot. An independent claim-file guard (O_CREAT | O_EXCL) additionally protects the artifact write itself, belt-and-suspenders alongside the lock, mirroring mixle.inference.production.registry.Registry.register()’s fix for the same class of bug.

Parameters:
Return type:

RegistryEntry

load(entry_id)[source]

Reload a registered model by entry_id (round-trips through the artifact on disk).

Always resolves through _get() against the registry’s own validated state – never a record a caller was handed by find_for() and may have altered.

Parameters:

entry_id (str)

Return type:

TaskModel | CalibratedTaskModel | Any

find_for(query, *, top_k=None)[source]

Entries matching query: a capability name (str, containment match) or a task fingerprint vector (array-like of floats, nearest-neighbor match). top_k caps how many are returned – every capability match by default, or the single nearest fingerprint match by default.

Returns defensive copies: the results are a view of the catalog, not handles onto it, so altering one cannot silently rewrite where a later load() reads from or which entries a later query matches (registration is the only way to change the catalog).

An entry whose fingerprint does not share the query’s dimension is skipped individually rather than aborting the whole search: one length-3 record used to make every two-dimensional query raise a broadcasting ValueError, hiding every healthy entry behind one incompatible one.

Parameters:
Return type:

list[RegistryEntry]

tier_stack(task, *, frontier, costs=None, names=None)[source]

Ascending-cost (name, model, cost) tiers for capability task, frontier appended last.

Matching entries are loaded (load()) and ordered by their effective cost – the costs override when given, else the registered per-entry cost. The result is exactly the shape Router takes as tiers=: each non-final tier exposes decide(x), the final tier is the callable frontier fallback. costs (one entry per matching solution in registered-cost order, plus one for frontier, mirroring from_solutions()) overrides the registered per-entry costs when given.

Parameters:
Return type:

list[tuple[str, Any, float]]

class RegistryEntry(entry_id, path, kind, capabilities=<factory>, fingerprint=None, profile=<factory>, cost=0.0)[source]

Bases: object

One catalog record: where the artifact lives, what it’s registered under, and how much it costs to run.

Frozen, and validated on construction. A durable catalog record is a persisted fact, not a scratch object: Registry.find_for() used to hand out the very objects stored in Registry._entries, so rebinding a lookup result’s path silently redirected the next Registry.load() and clearing its capabilities removed it from later matching – all without any registry mutation API, validation, persistence, or receipt. Lookups now return defensive copies (see copy()), and a rebinding attempt raises instead of quietly rewriting routing.

Parameters:
copy()[source]

A defensive copy: mutating its containers cannot reach the registry’s own state.

Return type:

RegistryEntry

to_dict()[source]

Serialize the registry entry into JSON-compatible fields.

Return type:

dict[str, Any]

classmethod from_dict(d)[source]

Create a registry entry from a JSON index record.

The index is untrusted persisted input, not a trusted in-process value: entry_id, kind, fingerprint and cost are validated here exactly as they are on Registry.register() (see Registry._read_index(), which additionally re-derives path from the registry root rather than trusting the serialized one).

Parameters:

d (dict[str, Any])

Return type:

RegistryEntry