mixle.registry module¶
Compatibility shim: mixle.registry moved to mixle.system.registry.
The local directory-backed model catalog (Registry, RegistryEntry) is now part of one cohesive
package, mixle.system, instead of six cross-importing top-level modules. Existing
from mixle.registry import X imports keep working (with a DeprecationWarning); new code
should import from mixle.system (or mixle.system.registry) directly.
- class Registry(dir)[source]
Bases:
objectA
dir-backed catalog of registered models:registerwrites an artifact + index entry;find_for/tier_stackquery it. Re-opening the samedirin 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 underdirand add its index entry; return the entry.modelis a fittedTaskModel, aCalibratedTaskModel, or amixle_pdefield posterior (a “field_posterior” kind, IC-2) – the three artifact-saveable model kinds.capabilitiesnames what this model answers (matched byfind_for());fingerprintis typicallytask_fingerprint()’s vector for the training data;profileis free-form (e.g. acapture_profile()dict);costis the per-request cost used to ordertier_stack(). An explicitentry_idthat 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_idmust be a single safe path component (_safe_entry_id()): an unvalidated value such as"../escaped"would otherwise write the artifact outsidedirinstead 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
Registryinstances opened on the samedir(or two threads/processes sharing one) each cache their ownself._entriessnapshot from construction time and otherwise never refresh it, so one instance’sregistercan 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 anfcntl.flockon 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, mirroringmixle.inference.production.registry.Registry.register()’s fix for the same class of bug.
- 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 byfind_for()and may have altered.
- 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_kcaps 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.
- tier_stack(task, *, frontier, costs=None, names=None)[source]
Ascending-cost
(name, model, cost)tiers for capabilitytask,frontierappended last.Matching entries are loaded (
load()) and ordered by their effective cost – thecostsoverride when given, else the registered per-entry cost. The result is exactly the shapeRoutertakes astiers=: each non-final tier exposesdecide(x), the final tier is the callablefrontierfallback.costs(one entry per matching solution in registered-cost order, plus one forfrontier, mirroringfrom_solutions()) overrides the registered per-entry costs when given.
- class RegistryEntry(entry_id, path, kind, capabilities=<factory>, fingerprint=None, profile=<factory>, cost=0.0)[source]
Bases:
objectOne 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 inRegistry._entries, so rebinding a lookup result’spathsilently redirected the nextRegistry.load()and clearing itscapabilitiesremoved it from later matching – all without any registry mutation API, validation, persistence, or receipt. Lookups now return defensive copies (seecopy()), 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.
- 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,fingerprintandcostare validated here exactly as they are onRegistry.register()(seeRegistry._read_index(), which additionally re-derivespathfrom the registry root rather than trusting the serialized one).