moca-reader
The MOCA Reader for Python: open a package from a directory, a .moca archive
or a host-supplied source; check it; compute its digest; verify attestations
and evidence; resolve members; answer structure operations; produce citation
records; and search with one entry point over lexical, dense-sidecar, store or
hybrid backends.
It implements the Reader class of the Reader contract, follows the Reader interface with Python names, passes the whole conformance corpus, and returns the same ranked results as the reference Reader, moca-reader-typescript.
Install
pip install moca-reader
Python 3.11 or later. Getting started takes you from install to a cited search result and a conformance run in five steps.
from moca_reader import Library, read_package
pkg = read_package("support-kb.moca", trust_root="trust-root.json")
print(pkg.valid, pkg.digest, pkg.capabilities)
library = Library().add(pkg)
for record in library.search("refund window", audiences=["public"]):
print(record["node"]["title"], record["node"]["ref"], record["score"])
A malformed package never raises: pkg.valid is False and pkg.diagnostics
says why. The Reader never reaches the network, or any file outside the
package, because a package names it. Citation records are plain dicts, the
same JSON every MOCA Reader produces (citation-record.schema.json).
The API
| Name | Purpose |
|---|---|
read_package(target, *, trust_root, resolve_member, strict, limits, known_profiles, now) |
Read and check a package: a path, a .moca/.zip path, or a host source with list() and read(path). |
Library(clock=, overlays=, limits=) |
Holds verified packages, with bound sidecar chunks: add, citations, get, search, structure. |
Search(source, backend, audiences=, hooks=) |
One entry point over a backend: search(query, limit=, include_all=, locale=, concepts=, scope=). The gate always runs last. source is a Library, or a StoreScope(package_states, clock) for a store backend: package_state(result) for each loaded package, so every library- and trust-dependent field is recomputed (contract §9.4). |
LexicalBackend, DenseBackend, MemoryStoreBackend, HybridBackend |
BM25 over package text or sidecar chunks; sidecar vectors with a host embedder (S006, S007); a reference in-memory store; Reciprocal Rank Fusion of a lexical and a dense backend. |
Embedder |
The protocol a host embedder follows: name, version, dimensions, query_prefix, document_prefix, embed(texts, role). |
moca_reader.embedders |
MOCA's four standard embedding models run locally (standard_embedder("bge-small-en-v1.5")), with the onnx extra. |
SearchHooks, ontology_guided(), diversify(per_node=) |
Retrieval hooks: host code, never package content. |
bind_sidecar(target, pkg) |
Check a sidecar against a package and return usable chunks. |
directory_resolver(dirs) |
A simple member resolver over folders. |
TrustRoot |
A host trust root, from a path or a dict. |
flat_metadata(record) |
The consuming guide's flat moca_* fields for a citation record, for vector stores. |
library.structure |
concept, requires, required_by, parts, narrower, broader, related, sequence, descendants, nodes. |
The API is synchronous.
Framework bindings
Separate packages, each a thin adapter over the Reader (ADR-0002):
| Package | What it gives you |
|---|---|
moca-reader-langchain |
MocaRetriever: a LangChain retriever returning Documents |
moca-reader-llamaindex |
MocaRetriever: a LlamaIndex retriever returning scored nodes |
Both wrap a Search you build, so the gate and your audience filter always
apply, and carry the whole citation record and the consuming guide's flat
moca_* fields in each result's metadata. flat_metadata(record) gives you
the same fields for your own vector store.
Search by meaning with a standard model
With the onnx extra, moca_reader.embedders runs MOCA's four standard
embedding models locally, the way every MOCA tool does, so a query embedded
here matches an index built with moca-index --model:
pip install "moca-reader[onnx]"
npx @openmoca/moca-embed models add bge-small-en-v1.5 # once: download and check the model
from moca_reader import DenseBackend, HybridBackend, LexicalBackend
from moca_reader.embedders import standard_embedder
embedder = standard_embedder("bge-small-en-v1.5") # files checked against their pinned SHA-256
hybrid = HybridBackend(LexicalBackend(library), DenseBackend(library, embedder))
Models are read from ~/.moca/models (or MOCA_MODELS). The Reader never
downloads them.
Lexical search
The built-in lexical backend is BM25 (k1 1.2, b 0.75) over node text, or over sidecar chunks when a sidecar is bound. Every MOCA Reader tokenises the same way, so they return the same results:
- NFKC-normalise, then lower-case each code point (simple case mapping).
- Letters, digits and combining marks make words; anything else separates them. Words in scripts with vowel signs, such as Devanagari, stay whole.
- Chinese, Japanese, Thai, Lao, Khmer and Myanmar text is split into each character (with its marks) and each pair of neighbouring characters.
- No stemming. A host that needs it supplies its own lexical backend.
- Without a locale, every language of a node is searched and its best match returned; with one, the representation package spec §5 selects.
Characters added in very recent Unicode versions can tokenise differently across Readers, because each language runtime ships its own Unicode data.
Conformance
As Reader contract §14 asks:
| Class | Reader |
| Contract version | 0.6.0 |
| Implemented | Archives, host sources, DSSE attestations, sidecars (lexical and dense), the ontology profile |
| Sigstore | Offline only, with the sigstore extra (pip install moca-reader[sigstore]), against the host trust root's sigstore.trustedRoot. Without the extra, or without a trusted root, a *.sigstore.json attestation is unverifiable (A003). There is no online mode: refresh the trusted root yourself (moca-spec attestations §6) |
The conformance adapter ships with the package. From a moca-spec checkout:
node scripts/run-conformance.mjs --reader "python -m moca_reader.conformance"
Develop
You need uv. The tests read moca-spec's examples
and run its conformance corpus. Check out
moca-spec beside this repository, or
set MOCA_SPEC_DIR. CI uses the tags pinned at the top of
.github/workflows/test.yml (MOCA_SPEC_REF and the like); check out the same
tags to match it.
uv sync
uv run pytest
uv run ruff check && uv run ruff format --check
uv run mypy
tests/parity/parity.mjs runs 18 search scenarios (87 queries) over
moca-spec's examples through this Reader and the reference Reader, and fails
on any difference in records, order, spans, text or scores. Dense sidecars use
a deterministic hashing embedder, so no model is needed. With
moca-reader-typescript and moca-tools (after npm ci) beside moca-spec:
node tests/parity/parity.mjs
Release
- Add a dated section to CHANGELOG.md and update
versioninpyproject.tomland__version__insrc/moca_reader/__init__.py. - Point the
MOCA_*_REFpins in.github/workflows/test.ymlat the release tags (moca-spec docs/releasing.md). - The tests, corpus and parity pass.
- Tag
vX.Y.Zand publish a GitHub release whose notes are the changelog section. uv build --all-packagesanduv publishfrom the tagged commit:moca-readerfirst, then its bindings, which release at the same version.
Licence
Apache-2.0. See LICENSE and NOTICE.
OpenMOCA™ is a trade mark of PI & Mash Solutions Ltd. The licence covers the code, not the name: see the trade mark policy.
Metadata
Release files for moca-reader 0.6.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| moca_reader-0.6.0.tar.gz | 409.2 kB | Without provenance | Details for moca_reader-0.6.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| moca_reader-0.6.0-py3-none-any.whl | Python 3 | none | any | Details for moca_reader-0.6.0-py3-none-any.whl |
Total release size: 516.5 kB
Release files / moca_reader-0.6.0.tar.gz
| Download URL | moca_reader-0.6.0.tar.gz |
|---|---|
| Size | 409.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fc37c66d65213cf2639601a7b6dbd80ba29b49e602cb787de01cf7511a8c18b0
|
|
BLAKE2b-256 checksum How to use checksums |
718a8e0660557140e0aa14e1b00711210f093b81be2df90326a266d97e7a0457
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / moca_reader-0.6.0-py3-none-any.whl
| Download URL | moca_reader-0.6.0-py3-none-any.whl |
|---|---|
| Size | 107.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f27f50fa2f5852905ea7bbb0a53fc1341815a44aba12e36d38522d41026d4e23
|
|
BLAKE2b-256 checksum How to use checksums |
750b981f979349b01c81f8552741499a85e1431c15a5b8175f63859b9e251e03
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for moca-reader 0.6.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.