mixle.substrate.ingest module¶
Ingest adapters: pull the stores the ecosystem already has into the knowledge substrate.
The substrate does not want copies of everything – it wants TYPED, PROVENANCED, RETRIEVABLE entries
pointing at what already exists. These adapters turn the three stores mixle already keeps into
SubstrateItem s:
ingest_documents– raw text / passages ->kind="text"items (the RAG corpus).
ingest_artifacts– a registry directory of deployed model/dataset artifacts (each amanifest.json) ->kind="artifact"items whose text surface is the manifest summary and whose payload references the artifact path (so lineage + retrieval work without copying weights).
ingest_traces– a harvested.jsonl(the/feedback/ agent-trace format) ->kind="trace"items (input->answer pairs for retrieval and curriculum).
Every item carries provenance (source path, kind, ingest time) so the reasoner can cite where a piece of knowledge came from.
Integrity, identity, and failure visibility (MXR-080-0268/0269): every function below returns an
IngestReceipt rather than a bare list[str] of successful ids. Three problems this fixes
together, because they share one root cause – nothing observed a per-input failure or recorded what
would let a LATER reader detect one:
Missing integrity data. When a doc/record/trace’s
source(or an artifact’smanifest.json) names a real, readable file, its provenance now carries a full, algorithm-labelledcontent_hash– byte-for-byte the same digestcontent_hash()produces, socheck_freshness()can actually detect a later byte change instead of silently having nothing recorded to compare against.Duplicate records instead of revisions. Every item’s id is now derived deterministically from
(kind, source, position)(see_stable_id()) rather than a fresh random UUID every call. Re-ingesting the SAME source reuses the SAME ids, soput()’s replace-by-id semantics turn a repeat ingestion into a REVISION of the existing records (a bumpedprovenance["revision"]counter, see_put_revisioned()) instead of an ever-growing pile of duplicates that all claim to be the current copy.Silent data loss. A malformed line, a manifest that is valid JSON of the wrong shape (a list where an object was expected), or any other per-input failure used to be swallowed by a bare
continue(or, worse, allowed to raise and abort the WHOLE batch after earlier inputs had already been stored – MXR-080-0269’s partial-write-then-crash). Everyingest_*function now validates each input’s shape BEFORE constructing/storing anything for it, and wraps that input’s handling so nothing it does can abort its siblings: one bad input becomes one named entry on the returned receipt’s.failed, and every other input in the same batch still lands. A caller can always tell “N succeeded, M silently failed” apart from “N+M succeeded” – and can always tell exactly what happened to every single input, never just “the call didn’t raise.”
- class IngestFailure(index, source, error)[source]
Bases:
objectOne batch input that did NOT make it into the substrate (MXR-080-0268/0269): its position in the batch, the source it came from, and why. Every
ingest_*function reports these on its returnedIngestReceiptinstead of silently skipping the input – “N succeeded, M silently failed” must never be indistinguishable from “N+M succeeded”.
- class IngestReceipt(ids=(), failed=())[source]
Bases:
listThe ids of every item actually ingested, in order – so
len(receipt),receipt[0],if receipt:,receipt == [...], andfor item_id in receipt:all mean exactly what they meant back when everyingest_*function returned a barelist[str]. That backward compatibility is deliberate, not an oversight: unlike a fresh@dataclassreceipt (e.g.FactualityReceipt), this one has EXISTING external callers that already index/measure it as a plain list of ids –mixle.reason.receipt._ingest_posterior()’sids[0] if ids else "",mixle.scientist.Scientist.learn()’slen(ingest_documents(...))– and both keep working completely unchanged against this type.PLUS the structured per-input accounting a bare list could never carry (MXR-080-0268):
.failednames every input that did NOT make it in, and why (seeIngestFailure);.okisTrueiff nothing failed. Before this fix, a malformed line or manifest was silentlycontinue-d past (or, for a wrong-shaped-but-valid JSON value, allowed to raise and abort everything after it – MXR-080-0269) and only the ids of what DID succeed were ever returned. A caller could not tell “3 succeeded, 2 silently failed” from “5 succeeded, nothing lost.” Now it always can, without losing any of the old call sites’ simplicity.- Parameters:
ids (Sequence[str])
failed (Sequence[IngestFailure])
- property ok: bool
Trueiff every input in this batch was ingested – no failures at all.
- merge(other)[source]
Combine two receipts (
ingest_file()’s.jsonlbranch fans one file out to bothingest_documents()andingest_records()): ids from both, in order, AND both.failedlists. Deliberately not done via+/+=– plainlistconcatenation only extends the id storage, silently dropping whichever side’s.failedentries were notself– exactly the kind of silent loss this whole fix exists to prevent.- Parameters:
other (IngestReceipt)
- Return type:
IngestReceipt
- ingest_documents(substrate, docs, *, source='documents', scope='local', indices=None)[source]
Add text passages to the substrate as
kind="text"items. Returns anIngestReceipt(the new/revised item ids, plus any per-doc failures – see its docstring; behaves exactly like the historical barelist[str]for every existing caller).Each doc is a string, or a
{"text": ..., "tags": [...], "payload": {...}}dict for metadata. Anything else is reported on.failedrather than crashing the whole batch (MXR-080-0269).Stable, revisioned identity and integrity (MXR-080-0268): each doc’s id is derived from
(source, its position)–indices[i]when the caller supplies one (soingest_file()can pass through a.jsonlfile’s true line numbers even after splitting mixed content into docs/records), else the plainenumerateposition. Re-ingesting the SAMEsourcereuses the SAME ids, turning a repeat call into a revision of the existing records (see_put_revisioned()) rather than a fresh random-id duplicate every time. Whensourcenames a real, readable file, every doc’s provenance also carries that file’s full, algorithm-labelledcontent_hash(the exact digestcheck_freshness()later compares against) –sourceis often just a label with nothing to hash (e.g. the"documents"default), so nocontent_hashis recorded for those.
- ingest_artifacts(substrate, registry_root, *, scope='local')[source]
Index every deployed artifact under
registry_root(dirs containing amanifest.json). Returns anIngestReceipt(behaves exactly like the historical barelist[str]).The item’s text surface is a human summary of the manifest (kind, io, meta); its payload REFERENCES the artifact directory (
{"ref": path}) rather than copying it – unchanged, since other readers (e.g.mixle.reason.receipt.decision_receipt()) depend onrefnaming the directory, not the manifest file – and provenance carries the manifest’s lineage fields when present.Integrity, identity, and validation (MXR-080-0268/0269): provenance also carries the manifest FILE’s full, algorithm-labelled
content_hash(content_hash()) and its ownmanifest_path, for anyone who wants to verify it directly – note that becausepayload["ref"](a directory) is whatcheck_freshness()actually re-hashes, an artifact item’s freshness verdict is UNVERIFIABLE by construction (a directory cannot be read as file bytes), which is the documented, intentional behavior of that check, not a gap in this one; recording the hash here is still correct and useful (forensic verification, and a future reader can compare it against a fresh hash of the same manifest file by hand). Each artifact’s id is derived from its directory path (see_stable_id()), so re-ingesting the SAME artifact directory revises the existing record instead of duplicating it. A manifest that fails to read, fails to parse, or parses to valid JSON of the wrong shape (anything other than a JSON object) is reported on.failed– checked BEFORE any.get()is called on it and BEFORE anything is stored for it – rather than raising and aborting every artifact discovered after it (MXR-080-0269).
- ingest_traces(substrate, jsonl_path, *, source=None, scope='local')[source]
Index a harvested
.jsonlof{"input": ..., "answer"/"label"/"call": ...}rows as traces. Returns anIngestReceipt(behaves exactly like the historical barelist[str]).Integrity, identity, and validation (MXR-080-0268/0269): every row’s provenance carries the source file’s full, algorithm-labelled
content_hash(one read of the whole file up front, reused for every row – all rows from the same file share the same recorded hash, since a byte anywhere in the file invalidates every row’s “as ingested” guarantee equally). Each row’s id is derived from(source, its line number), so re-ingesting the SAME file revises the existing per-row records instead of duplicating them. A line that fails to parse, or parses to valid JSON that is not a JSON object (the documented row shape requires.get()-able fields), is reported on.failed– checked BEFORE any.get()is called on it – rather than raising and aborting every row after it in the file (MXR-080-0269, the exact “trace code assumes every row is a mapping” bug).
- ingest_records(substrate, records, *, source='records', scope='local', text_fields=None, indices=None)[source]
Add a sequence of records (dicts or tuples) to the substrate as
kind="record"items. Returns anIngestReceipt(behaves exactly like the historical barelist[str]).Each record’s payload is stored structured; its retrievable text surface is the
text_fields(for dict records) joined, else the whole serialized record – so records are searchable by content.Stable, revisioned identity and integrity (MXR-080-0268), mirroring
ingest_documents(): each record’s id is derived from(source, its position)–indices[i]when supplied (seeingest_file()), else the plain position – so re-ingesting the SAME source revises the existing records instead of duplicating them, and a real, readablesourcefile’scontent_hashis recorded on every record’s provenance. One record’s storage failure (e.g. a rejected secret undersecret_policy="reject") is reported on.failedrather than aborting the rest of the batch.
- ingest_file(substrate, path, *, kind=None, source=None, scope='local')[source]
Ingest a data file into the substrate. Format inferred from the extension unless
kindforces it. Returns anIngestReceipt(behaves exactly like the historical barelist[str])..txt/.md-> onetextitem per non-empty line;.jsonl-> one item per JSON line (a string /{"text": ...}becomes a text item, any other object a record item);.csv-> onerecorditem per row keyed by the header.sourcedefaults to the file path, so every resulting item picks up that file’scontent_hashand a stable, revisioned identity (seeingest_documents()/ingest_records()) automatically.A malformed
.jsonlline is reported on the returned receipt’s.failed(naming its 1-based line number) rather than silently dropped (MXR-080-0268) or allowed to abort every line after it (MXR-080-0269); the.jsonlbranch preserves each row’s TRUE original line number as its stable identity even after splitting mixed content into a docs sub-batch and a records sub-batch (viaIngestReceipt.merge(), which – unlike plain list concatenation – keeps both sub-batches’.failedentries instead of silently dropping one side’s).