[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "llm-ingestion-okf" version = "0.5.0a2" description = "Shared OKF (Open Knowledge Format) ingestion library: spec-based connectors, bundle inbox, and external-bundle import, with security delegated to llm-ingestion-guard." readme = "README.md" license = "MIT" requires-python = ">=3.10" authors = [{ name = "Kjell Tore Guttormsen" }] classifiers = [ "Development Status :: 2 - Pre-Alpha", "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", ] # Exactly one runtime dependency, ever: the security boundary. Everything # else is stdlib. The version range is the real pin — it resolves normally # against a package index, and is satisfied today by the git+https tag # install documented in the README (a direct reference is an install-time # channel, not a dependency declaration). dependencies = ["llm-ingestion-guard>=0.3,<0.4"] [project.optional-dependencies] # Binary file-type extraction parsers. OPT-IN ONLY: this extra pulls binary # wheels (pillow, pypdfium2) and a transitive tree that core must never have — # the "exactly one runtime dependency" rule above covers the default install, # and this extra is outside it by construction. # # `pdf` only. `docx`/`xlsx` remain fail-fast: the extra names the parsers it # actually ships, so a consumer installing it gets what the error message # promised and nothing else. # # WHY pdfplumber, and why the floor is not free (measured 2026-08-21, # docs/2026-08-21-g2-pdf-extraction-measurement.md): on a real Vegnormalene # requirement table pdfplumber keeps 4 of 4 rows with label and value on the # same line; pypdf, pdfminer.six and pymupdf each keep 0 of 4, emitting all # labels then all values, which a downstream reader can only re-pair by # guessing. In a `krav` document that is a wrong answer that looks right. # pymupdf is additionally out on LICENSE (AGPL-3.0 or commercial) — this # package is MIT and an extra must not hand a consumer copyleft they did not # choose. # # PARSER VERSION IS PART OF THE OUTPUT CONTRACT. pdfplumber pins # `pdfminer.six==20260107` exactly, and pdfminer.six ships date-stamped # releases with no stability contract. Extraction is deterministic WITHIN a # parser version (measured, 5 configurations) and NOT guaranteed across one. # `tests/test_extract.py` holds that promise against a committed fixture, so # widening this range makes a test go red instead of letting extracted text # drift silently. See tests/fixtures/README.md. extract = ["pdfplumber>=0.11.10,<0.12"] [dependency-groups] dev = ["pytest>=8", "mypy>=1.14", "ruff>=0.9"] [tool.hatch.build.targets.wheel] packages = ["src/llm_ingestion_okf"] [tool.ruff] line-length = 100 target-version = "py310" [tool.mypy] strict = true python_version = "3.10" # llm-ingestion-guard ships no py.typed marker, so its symbols arrive as Any. # The adapter coerces every value it carries across the seam to a concrete # type, which is what keeps --strict meaningful on this side of it. [[tool.mypy.overrides]] module = ["llm_ingestion_guard", "llm_ingestion_guard.*"] ignore_missing_imports = true # Install CHANNEL for the guard, which is not on a package index yet. It is # uv-specific, and it reaches further than a dev-only setting: a consumer # installing this package from git WITH UV picks the guard up from this tag # automatically, because uv reads this file when it builds from the source # tree. Measured against an empty cache 2026-07-25, 2026-08-20, and # 2026-08-21 on uv 0.9.8. The 08-21 run also measured the TRANSITIVE form: a # separate consumer project naming only this package still resolves the guard # from the entry below, because this package reaches it as a git source. # # That source is the whole reach. A wheel carries Requires-Dist and nothing # else, so this entry cannot survive an index install — and while the guard is # off-index, removing it would break the one-command uv path the README # documents. # # pip does not read it at all: it resolves [project.dependencies] alone and # fails with "No matching distribution found for llm-ingestion-guard" until # the guard is installed from its own tag first (README; measured 2026-08-21, # both the failure and the two-command recovery). # # Either way the range above stays the pin, and the pin is per-tree: a wheel # built from THIS tree carries `Requires-Dist: llm-ingestion-guard<0.4,>=0.3`, # measured 2026-08-21 against the built wheel. The `<0.3,>=0.2` this comment # carried before was the `v0.4.0` tag's range — still true of that tag, never # true of this tree. Reading a range off one and installing it against the # other is the one combination that fails. [tool.uv.sources] llm-ingestion-guard = { git = "https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git", tag = "v0.3.4" }