- Python 100%
Operator directive 2026-07-26: consumers are better served by getting the latest version early and reporting back than by us holding it until we judge it finished. This closes a real gap. Every test in the plan -- including V-A8 against upstream's reference implementation -- asks whether output is conformant. None asks whether it is usable: whether a bundle is awkward to construct, whether a rejection message is actionable, whether the profile can express what a consumer's actual data needs. Only real data surfaces that. Pilot set is three repos, one axis each, chosen for signal: portfolio-optimiser-claude for the producer path (one real manifest run), catalog for gate acceptance (their gate on our fixture, measured rather than inferred from the regex), and claude-code-llm-wiki for expressiveness across 522 real documents. The load-bearing design detail is that most of the pilot is read-only on our side: we run the v0.2 reader over real consumer bundles and send the report. That needs no adoption, no writes into their trees (O2 holds), and no change to a contract their operator ratified. Only the producer axis asks a consumer to do anything, and it asks for one run. Excluded with reasons rather than silently: okr (Node side not yet lifted), linkedin-studio (v0.2's provenance families would put implicit pressure on the ingest/published carve-out we agreed not to normalize), commons (they are deciding V1 -- a participant, not a test site). Shipping a provisional surface without owing stability rests on three rules, not on saying "provisional": OKF_LATEST does not point at v0.2 until GA, so flipping it is the GA event rather than a merge side effect; the docstring and CHANGELOG name the pilot repos; and breaking changes during the pilot get no deprecation cycle. Stating that last one up front is what buys the freedom to act on feedback -- discovering it later is what turns a pilot into a de-facto release. Feedback is requested in five named parts, because unstructured reports are not comparable across three repos. The one that matters most is "what was awkward but worked": workarounds are the highest-value signal and never appear as a failure. GA exit criteria are testable, and carry one honest limit: a three-repo pilot exercises only what those three use, so `sources` with usage_window, multi-verifier `verified`, and Attested Computation will likely go unexercised. Those stay marked provisional at GA instead of being silently promoted -- claiming otherwise would be the same unearned-claim pattern that "conform first, claim after" exists to prevent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A2aKJxLejT9S8jYwoZ9fut |
||
|---|---|---|
| docs | ||
| examples | ||
| src/llm_ingestion_okf | ||
| tests | ||
| .gitignore | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
llm-ingestion-okf
Shared ingestion library for OKF (Open Knowledge Format) bundles.
Status: phases 1 and 2 are implemented. Phase 1 (spec-based ingestion) covers
manifest validation, the file/sql/http connectors, deterministic
materialization, index generation, and the golden fixture suite under
examples/. Phase 2 adds the bundle inbox (process_inbox) and
external-bundle import (import_bundle), both against an injected persist
gate, with llm_ingestion_okf.guard_adapter wiring that gate to the real
guard (see below). One phase-2 item is deliberately outstanding: binary
extraction (pdf/docx/xlsx behind the [extract] extra) is unimplemented,
so those types are rejected fail-fast. Phases 3–4 are planned (see
docs/plan/).
Planned scope (v1)
The library provides three entry points for getting content into an OKF bundle:
- Spec-based ingestion. An implementation of the normative ingest
specification owned by
portfolio-optimiser-commons: manifest →file/sql/httpconnector → deterministic materialization ofingest-{id}.mdconcept files → index generation. Zero model calls in the run path; output is reproducible byte-for-byte against golden fixtures. - Bundle inbox. A drop directory where common file types are converted
to OKF concept files. All file-type→text extraction lives in this library:
md,txt,csv,json, andhtmlare handled by the stdlib core;pdf,docx, andxlsxrequire the optional[extract]extra and are rejected fail-fast without it. Extracted text passes the security gate before anything is persisted. - External bundle import. Import and merge of third-party OKF bundles: each concept is assessed via the security gate, and only concepts that pass are merged, materialized, and linked into the index.
Boundary: security is delegated
Security is owned by the sibling package
llm-ingestion-guard
(pinned >=0.2,<0.3). The division is strict:
- guard answers "is this content safe to persist?" — scan, sanitize, quarantine, fail-secure, provenance stamping.
- this library does the plumbing — connect a source, materialize a deterministic OKF bundle, generate the index.
No security functionality is reimplemented here.
What is gated today: read this before trusting a door
- Door A (
materialize_bundle) is ungated. It calls nothing before writing to disk and writes what it is given. A caller materializing untrusted content is responsible for gating it. - Doors B and C (
process_inbox,import_bundle) gate through an adapter you pass in. Each takes agateargument; the flow hands it the content and obeys the verdict, refusing to persist anything that does not clear the guard's non-blocking floor — including a disposition it does not recognise, and (at Door C) a concept the gate returned no verdict for. What it cannot do is check that your adapter is a real guard: a permissive stub approves everything, and the flow will believe it.
llm_ingestion_okf.guard_adapter is the adapter over the real guard, and the
only module here that imports it — importing the package itself does not:
from llm_ingestion_okf import process_inbox
from llm_ingestion_okf.guard_adapter import inbox_gate
result = process_inbox(inbox_dir, bundle_dir, "2026-07-25T12:00:00Z",
okf_type="reference", gate=inbox_gate)
Two properties of that adapter are worth knowing before you rely on it.
It screens the exact bytes it persists — the guard's prepare_input
bookend prepares text for a model call, which this library never makes, so
only screen_output is used and the screened string is the written string.
And it refuses rather than repairs: a file carrying an invisible
zero-width or bidi character is rejected, not silently stripped and written.
Door B screens under the untrusted-upload policy, so any finding at all is
held back rather than persisted.
This section is stated plainly because earlier wording ("calls the guard at every persist gate") described the intended end state in the present tense, and a consumer reasonably read it as safe-by-default.
Roadmap
The library is built in four phases so that every known OKF surface in the ecosystem is eventually covered. Each phase has a detailed plan with verification criteria:
- Spec-based ingestion (Python) with byte-exact golden fixtures — plan.
- Bundle inbox and external-bundle import (Python), guard-gated — plan.
- Configurable bundle contract (types, layers, frontmatter sets, index
shape, and reserved-file policy as configuration), enabling stricter
bundle profiles such as
strict-v1— plan. - A
node/half: a zero-dependency Node/ESM package (importable and CLI-invokable, vendored per consumer) providing bundle checking, index generation, inbox processing, and document conversion for the OKF second-brain plugin ecosystem. The Python and Node halves share the OKF contract and fixture suite, not code — plan.
Non-goals
- Verdict/feedback machinery from the method specification (stays in the consuming repositories).
- Embedding- or retrieval-layer functionality.
- Security functionality, in either runtime — that is always
llm-ingestion-guard's domain.
Requirements
Python 3.10+, and exactly one runtime dependency — the security boundary,
llm-ingestion-guard>=0.2,<0.3. Everything else is stdlib.
That guard is not on a package index yet, so with pip, install it first —
otherwise installing this package fails with No matching distribution found for llm-ingestion-guard:
pip install "llm-ingestion-guard @ git+https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git@v0.2.0"
pip install "llm-ingestion-okf @ git+https://git.fromaitochitta.com/open/llm-ingestion-okf.git@v0.4.0"
With uv, one command is enough — uv pip install "llm-ingestion-okf @ git+…@v0.4.0"
resolves the guard from the tag on its own, because uv reads the
[tool.uv.sources] entry in this project's pyproject.toml when it builds
from the source tree. Both paths were measured on 2026-07-25.
A git URL is a PEP 508 direct reference and pins one exact tag, so it is an
install-time channel, not the pin: the range above stays the declared
dependency — the built wheel carries Requires-Dist: llm-ingestion-guard<0.3,>=0.2 — and resolves normally once the package index
exists. The optional [extract] extra (pdf/docx/xlsx parsers) is not
populated yet. The planned Node half targets Node/ESM with zero npm
dependencies.
License
MIT — see LICENSE.