mixle.system package¶
Local application facade: answer/ingest/improve a Mixle system, with its cost ledger, degradation policy, held-out scorecard, meta-improvement allocator, and model registry.
Formerly six cross-importing top-level modules (mixle.system, mixle.spend, mixle.fault,
mixle.scorecard, mixle.meta, mixle.registry) – one cohesive package now:
mixle.system.core–System,SystemConfig,Query: the answer/ingest/improve facade.mixle.system.spend–Spend: the budget ledgerSystemis metered against.mixle.system.fault–with_fallback(),abstain_on_timeout(),route_past(),DegradedResult: named degradation modes instead of silently-successful fallbacks.mixle.system.scorecard–SystemScorecard,evaluate(),detect_regression(): measuring aSystemagainst a held-out question set.mixle.system.meta–ImprovementOption,improve_by_regret(): spend an improvement budget on the highest realized gain-per-dollar option, stopping on regression.mixle.system.registry–Registry,RegistryEntry: the local directory-backed catalog of fitted task models these workflows read from and write into.
Every name below is importable directly from mixle.system (from mixle.system import System,
Spend, Registry); the individual submodules remain the place to look for the source and docstrings.
The old flat top-level names (mixle.spend, mixle.fault, mixle.scorecard, mixle.meta,
mixle.registry) still work via a deprecation shim; new code should import from here.
Resolution is lazy (PEP 562, mirroring mixle itself): importing one submodule – e.g.
mixle.system.fault, which callers such as mixle.doe.oracle use for its zero-dependency
degradation helpers – never pulls in the other five. core.py alone reaches mixle.task; an
eager __init__ that imported every submodule up front would force that whole chain onto any caller
of the leaf modules, including a cycle back through mixle.task.propose.
- class DegradedResult(value, degraded, mode=None, reason=None, attempts=())[source]
Bases:
objectThe outcome of a fault-boundary call: a value, whether it came from a fallback, and, if so, why.
attemptscarries the structured causal evidence for a multi-tier route: one(tier_name, exception_repr)pair per tier that was tried and failed, so a receipt records what actually went wrong rather than only that something did.degraded,modeandreasonare one fact in three fields, so they are checked against each other at construction: a degraded result names its mode and carries a reason, and a non-degraded one carries neither. Without that,DegradedResult("v", False, "teacher_down", "failure")serialized a receipt asserting that a successful answer was produced by a degradation, andDegradedResult("v", True)one asserting a degradation nobody can attribute – both of which downstream audit code reads as ordinary records.modeis deliberately NOT restricted toDEGRADATION_MODES. This is a reusable fault boundary and callers name their own routes (mixle.inference.production.servingdegrades undermodel_unavailable/batch_unavailable); a closed set would turn an unlisted caller’s recoverable failure into a hard constructor error on the fallback path, which is precisely the outage this class exists to survive. The name must simply be a real one.- Parameters:
- class ImprovementOption(name, cost, run, estimated_regret)[source]
Bases:
objectCandidate improvement action with estimated cost and recoverable quality gain.
costmust be finite and nonnegative andestimated_regretfinite. Neither was checked, so underbudget=0an option costing-5ran and reportedspent=-5(running work created budget), and a NaN-cost option ran too, because the over-budget comparisonspent + cost > budgetis false for NaN – the ceiling failed open – and leftspent=NaNbehind.- property regret_per_dollar: float
Estimated recoverable gain per unit cost.
- class MetaImprovementReport(order=<factory>, skipped=<factory>, scorecard_before=None, scorecard_after=None, realized_gain_per_dollar=<factory>, spent=0.0, stopped_on_regression=None)[source]
Bases:
objectExecution report for a budgeted meta-improvement run.
- class Query(text, task='', fingerprint=None, expected_output=None, scope='local')[source]
Bases:
objectThe typed problem contract for
System.answer().taskandexpected_outputalign with themixle-knowledgeContextPacketcontract’staskandexpected_output_schemafields (seefrom_knowledge_dict()).scopeis aQuery-level routing boundary and is not inferred from the packet.- Parameters:
- classmethod from_knowledge_dict(packet, *, scope='local')[source]
Build a
Queryfrom a mixle-knowledge-shapedContextPacketdict.textcomes frompayload["rendered"].taskandexpected_outputmap from the packet’staskandexpected_output_schema.scopeis supplied by the caller.
- 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).
- class RegressionReport(regressed, reasons=<factory>, comparable=True)[source]
Bases:
objectWhether
currentis worse thanbaselineon any tracked axis, and exactly why.comparableis False when the two cards are not evidence about the same thing (different held-out set/judge, different case counts, an unidentified card). An incomparable pair is reported asregressed=True: a comparison that could not be made is not a passed comparison.
- class Spend(frontier_calls=0, oracle_calls=0, wall_ms=0.0, dollars=0.0)[source]
Bases:
objectA summable cost total.
total_units()is the scalar figurebudget=is checked against.Every dimension is checked on construction, and therefore again after each
__add__()and after deserialization, because a receipt is a ledger: work only ever consumes budget. Without these invariantsSpend(frontier_calls=-5).total_units()returned-5, so doing work could hand budget back, and a NaNwall_msserialized as an ordinary receipt field while making every hard-ceiling comparison against it false – failing open on the check meant to stop overspending.- total_units()[source]
The scalar cost a
budget=integer is measured against.Currently
frontier_calls + oracle_calls– the two countable, per-call costs the existing routes measure budget in.wall_msanddollarsare carried and reported on every receipt but are not yet priced into the hard-ceiling check; extend this method when a concrete dollar cost model is introduced.- Return type:
- class System(config)[source]
Bases:
objectConstructed from a
SystemConfig; exposesanswer/ingest/improve.- Parameters:
config (SystemConfig)
- answer(query, *, budget=None, read_only=False)[source]
Thin shell: route straight to the teacher, wrap the reply in a minimal H-style receipt.
Checks the captured cache first (see
improve()): an exact repeat of a query (same text, task, AND scope – two queries that merely share text but differ in task/scope are different questions and must not share a cache entry) already promoted by a priorimprove()call is served free, no budget spent,captured=True.budgetis a hard ceiling (total_units): if it cannot afford even one frontier call, the request is refused –replyisNoneand the receipt names the exactshortfall– rather than silently answering over budget. A served answer’s cost is added tototal_spend, which every receipt also carries astotal_spend.If the teacher call itself raises, this falls back to
teacher_downdegraded mode: answer from the store alone (a plain retrieval overconfig.store, scoped to the QUERY’s ownscope– neverconfig.scope) when one is configured and has anything relevant, flaggingdegraded_mode="teacher_down"on the receipt; if there is no store (or nothing relevant in it), the failure is reported explicitly (status="failed"), never masked as a normal answer.read_only=Trueruns the identical routing/answering logic but takes a genuine snapshot: the call is guaranteed not to promote into_harvest(so a laterimprove()can never capture it) and not to accumulate intototal_spend– no trace of the call is left for the system’s future behavior to depend on. This is whatevaluate()uses so that scoring a system against a held-out question set can never itself teach the system the held-out answers: an evaluation call must never be able to change what a later call to this same method returns. Every other observable (the reply, the receipt fields a scorer reads, whether the call degraded) is identical to a normal call, since a snapshot must still faithfully measure the real answering path.budgetis validated as an exact, non-Boolean, nonnegative call count – the same_count()contractSpendholds its own dimensions to, since the ceiling is compared directly againsttotal_units()(MXR-080-1902).int(budget)TRUNCATED instead:budget=1.9silently became a one-call ceiling andbudget=Truebecame one too, and the receipt then reported the truncated number as if it were what the caller asked for. A negative budget is a caller error and raises, matchingimprove(), rather than being served back as an ordinary “refused, shortfall N” receipt that reads like a legitimately under-funded request.
- ingest(model_output, *, source)[source]
Turn a model output into stored knowledge.
Uses the belief store when it is importable; otherwise records a plain substrate item rather than requiring optional knowledge-substrate components.
Every route writes under
SystemConfig.scope, and the effective scope is named on the report. The belief path used to callassimilate()without itsscopeargument, so it defaulted to"local"no matter how the system was configured – a system configured for"tenant-A"ingested two claims and got back two apparently successfulBeliefItem``s whose scope, and whose stored substrate scope, were both ``"local". The import-fallback path did useconfig.scope, so which boundary a write landed in depended on whether an optional module happened to be importable.If the store write raises before anything is committed, this falls back to
store_downdegraded mode: the model output is acknowledged but not accumulated, and the report is flagged withdegraded_mode="store_down".Claims are assimilated one at a time and there is no transaction, so a failure partway through leaves the earlier claims stored. That case is reported as
status="partial_accumulation"with the exact committed items, not asdegraded_no_accumulation: denying durable state invites a retry that duplicates the committed evidence, or reasoning from a receipt that says nothing was written when something was.
- improve(budget)[source]
Promote harvested (query, reply) pairs from
answer()into the captured cache, up tobudget.Promotion is costed at a flat
_PROMOTE_COST_PER_ITEM(currently 1) unit per (query, reply) pair – the only cost model defined for this step, since promoting a harvested pair is a pure cache write, not an external call like the frontier/oracle spendanswer()meters.budgetis a hard ceiling on how many pairs THIS call may promote, the same hard-ceiling spirit asanswer’s budget: a negative budget is a caller error and raisesValueErrorrather than being silently treated as zero or unlimited.Candidates are promoted in harvest order (the order
answer()first harvested them – harvested pairs carry no other recency or confidence signal to rank by, so this is the simplest available, documented priority). Whatever does not fit the budget is left in the harvest for a later, better-funded call rather than being discarded.Reports that there is nothing to improve when nothing has been harvested yet. Otherwise this is the cold-start capture step: after this call, a repeat of a captured query is answered from the local cache (see
answer()). The return value reports both the requested ceiling (budget) and the realized spend (realized_spend) so a caller can tell a fully-funded round from a partial one.budgetis an exact, non-Boolean, nonnegative promotion count (_count(), MXR-080-1902).int(budget)truncated first and only then rejected a negative, soimprove(1.9)quietly promoted one pair against a budget the caller wrote as nearly two,improve(True)promoted one, andimprove(-0.5)truncated to0and returned an ordinary “insufficient_budget” report instead of naming the error that the same call written asimprove(-1)raises for.
- class SystemConfig(teacher, registry_dir=None, store=None, default_budget=1, scope='local')[source]
Bases:
objectConfiguration required to run a
System.Secrets such as endpoints and keys are read from the environment by
from_env(); they are not hardcoded in the config object.- Parameters:
- classmethod from_env(*, store=None, registry_dir=None)[source]
Build a config whose teacher is an
OpenAICompatLLMsourced entirely from env vars.Reads
MIXLE_TEACHER_BASE_URL(required),MIXLE_TEACHER_MODEL(required), and the optionalMIXLE_TEACHER_API_KEY. RaisesValueErrornaming the missing variable rather than silently constructing an unusable teacher.
- class SystemScorecard(quality, realized_cost, grounded_fraction, n, question_set_id='')[source]
Bases:
objectOne evaluation of a
Systemagainst a fixed held-out question set.Every metric is validated on construction: a card carrying NaN/out-of-range numbers or a negative case count is not a weaker measurement, it is not a measurement at all, and
detect_regression()silently treated such a card as “no regression” (fail-open on the exact gate meant to catch a worsening round).question_set_idbinds the card to the held-out set and judge it was measured with (seequestion_set_identity()).- Parameters:
- abstain_on_timeout(fn, *, timeout_error=TimeoutError)[source]
oracle_timeoutmode: runfn(); if it raisestimeout_error, abstain (value=None) rather than guess. Other exceptions propagate.The abstention records its
DegradedResult.attemptslike every other degraded result this module produces (MXR-080-1902). It used to returnattempts=(): anoracle_timeoutabstention was the one degradation whose receipt carried no structured causal evidence at all, so audit code walkingattempts– the fieldDegradedResultdocuments as “the structured causal evidence” – saw an empty tuple and could not distinguish a timed-out oracle from a route that never degraded, even thoughwith_fallback()androute_past()both record theirs.reasonalone is a human-readable string, not the same thing.- Parameters:
timeout_error (type[BaseException])
- Return type:
DegradedResult
- detect_regression(baseline, current, *, tolerance=1e-9)[source]
Compare two scorecards from the SAME held-out set across improve-rounds.
Never silently accepts a round that answers worse, less groundedly, or for more cost than the round before it – each tracked axis that got worse (beyond
tolerance) is named inreasons.The “same held-out set” precondition is enforced, not merely documented: both cards must carry the same non-empty
question_set_identity()and the same case count. Comparing a card against evidence from a different (or empty) question set cannot show that a round did not regress, so such a pair comes backcomparable=False, regressed=Truerather than a clean bill of health.tolerancemust itself be finite and nonnegative – a NaN tolerance makes every ordered comparison below false, which silently turned this gate into an unconditional “no regression”.- Parameters:
baseline (SystemScorecard)
current (SystemScorecard)
tolerance (float)
- Return type:
RegressionReport
- evaluate(system, question_set, *, scorer=None)[source]
Evaluate
systemover a fixed[(query, expected_answer), ...]set.Every
expectedis validated up front: a blank reference cannot discriminate, and a question set containing one is rejected rather than scored (it used to make every non-Nonereply correct and reportquality=1.0).quality– fraction of questionsscorerjudges correct. The default judge is_default_scorer(), a deliberately weak lexical baseline (case-insensitive substring match) that cannot see negation or paraphrase; read its docstring before treatingqualityas authoritative, and pass a task-specificscorerwhere correctness actually matters.grounded_fraction– fraction answered WITHOUT a degraded mode (teacher or captured, not a store-only fallback / refusal / failure): the fraction of answers you can actually trust came from a real answer path, not a fault-boundary guess.realized_cost– total spend units (total_units()) across the set.question_set_id– the identity of the held-out set and judge this card was measured with, sodetect_regression()can refuse to compare cards from different evidence.
There is deliberately NO
calibrationfield. One used to exist and was assignedqualityverbatim: a deterministic system that emits no confidence at all scored a perfect “calibration”, anddetect_regression()never compared the field anyway, so a drop from 1.0 to 0.0 was not a regression.answer()carries no per-answer confidence to calibrate against, so there is nothing here to compute; when it does, add a real calibration statistic to THIS function and a matching axis todetect_regression()in the same change, rather than re-adding a copied number that reads as evidence of something never measured.This is a genuinely read-only pass: every call goes through
answer()withread_only=True, so evaluating a held-outquestion_setcan never itself teachsystemthe held-out answers (no promotion into the harvest a laterimprove()could capture, no change tosystem.total_spend). Without that, a held-out set would stop being held-out the moment it was first evaluated: a laterimprove()could promote its answers into the captured cache, and re-evaluating the “held-out” set afterward would be trivially free and perfect. Never callsystem.answerwithoutread_only=Truefrom this function.
- improve_by_regret(system, question_set, options, *, budget)[source]
Run options by estimated gain per dollar and stop on measured regression.
budgetmust be finite and nonnegative, and option names must be unique –order,skippedandrealized_gain_per_dollarare all keyed by name, so duplicates silently overwrite each other’s measured result.Note what this does NOT do:
opt.run()mutates the livesystembefore any measurement, and a regression detected afterwards only stops further options – the regressing change stays applied. Callers who need rejection to restore the previous state must run options against a system they can themselves discard.report.stopped_on_regressionnames the round that regressed so that decision can be made.
- route_past(tiers, *, names=None, recoverable=None)[source]
model_errormode: try each tier in order; a tier that fails recoverably is skipped (not fatal) in favor of the next. The result is degraded unless the first tier answers cleanly. Raises the last tier’s exception if every tier fails.recoverableis an explicit allowlist of failure classes this route may skip a tier for; omit it for the default policy (anything exceptNON_RECOVERABLE_FAULTS). A non-recoverable failure propagates immediately instead of being reclassified asmodel_error: an internalTypeErrorrouted past becomes the next tier’s ordinary answer and the defect is never seen, and skipping aPermissionErrorturns a denial into a served result from a tier the caller was not authorized to reach.Every skipped tier is recorded on
DegradedResult.attemptsas a(tier_name, exception_repr)pair – the causal evidence for why the answer came from where it did, not merely that it was degraded.
- with_fallback(fn, fallback, *, mode, recoverable=None)[source]
Run
fn(); on a recoverable exception, runfallback(exc)instead and flag it undermode.recoverableis an explicit allowlist of failure classes this route may degrade past; omit it for the default policy (anything exceptNON_RECOVERABLE_FAULTS). A non-recoverable failure propagates unchanged: an internalTypeErrorhanded back as a fallback answer hides a defect, and aPermissionErrordegraded into a successful-looking reply bypasses a fail-closed boundary rather than reporting that access was denied.If
fallbackitself raises, that exception propagates. A fallback that cannot produce a value is a real failure, not a second implicit fallback.modeis checked BEFOREfnruns: an unnamed mode makes the eventual receipt unattributable, and discovering that only on the fallback path would convert a survivable outage into a hard failure at exactly the wrong moment.