Dependency-completeness audit¶
Purpose. Prove that every package under packages/ declares every third-party
runtime dependency it actually imports, so a clean pip install <package> (with
its resolver, no --no-deps) on a fresh machine never hits a ModuleNotFoundError.
This is the audit that would have caught the peft gap the pod validation found
(gliner2's inference engine imports peft, which gliner2[local] does not declare —
fixed in commit cdbb8c2, before this audit).
Method (repeatable). For each packages/<pkg>/src/, every top-level third-party
module name is parsed out of the actual import X / from X import … statements
(vendored _vendor/ trees and relative imports excluded), mapped import-name →
distribution-name where they differ (yaml→pyyaml, PIL→pillow, docx→
python-docx, pptx→python-pptx, sentence_transformers→sentence-transformers,
presidio_analyzer→presidio-analyzer, torch_geometric→torch-geometric), then
checked against that package's [project].dependencies + [project.optional-dependencies],
counting a dep as covered if it is declared directly, is a documented optional
extra guarding a lazy import, or is a transitive dep of a declared latence-* sibling
(latence-core declares pydantic, fsspec, pyarrow, pyyaml, typer — packages
that depend on core inherit those and need not re-declare them). Script:
scripts/dep_audit.py (re-run to regenerate this table).
Guarded vs unguarded. A module-level import must be a hard-declared dependency.
A nested (inside-a-function) import is a lazy/optional load and is legitimately
covered by an optional extra with a graceful ImportError message — that is the
established pattern here (airflow, the ULTRA/PyKEEN heavy stack), not a gap.
Result¶
No genuinely-undeclared runtime dependency was found. Every module-level
third-party import is covered by a declared dep or a latence-core-transitive dep.
The two items the scan flags as "not directly declared" are both guarded (lazy)
imports that are intentional, documented design (see notes below), not the peft
class of bug. The peft gap itself is already fixed on wave-integration.
Per-package table¶
Covered-by: declared = in this package's dependencies; core = transitive via
latence-core; extra[x] = declared under optional-dependency group x, import is
lazy/guarded.
| Package | Third-party import | Distribution | Module-level? | Covered by |
|---|---|---|---|---|
| latence-core | fsspec |
fsspec | yes | declared |
| latence-core | pyarrow |
pyarrow | yes | declared |
| latence-core | pydantic |
pydantic | yes | declared |
| latence-core | typer |
typer | yes | declared |
| latence-core | yaml |
pyyaml | yes | declared |
| latence-core | torch |
torch | no (lazy) | intentionally undeclared — near-zero-core (ADR-0016); see note 1 |
| latence-demo | pydantic |
pydantic | yes | core |
| latence-demo | typer |
typer | yes | core |
| latence-disambig-embedding | (none) | — | — | pure latence-core consumer |
| latence-embedder-endpoint | openai |
openai | lazy | declared |
| latence-embedder-st | sentence_transformers |
sentence-transformers | lazy | declared |
| latence-extract-gliner2 | gliner2 |
gliner2 | lazy | declared (+ peft runtime dep, note 3) |
| latence-linkpred-ultra | easydict |
easydict | lazy | extra[ultra] — LGPL flag, note 4 |
| latence-linkpred-ultra | pykeen |
pykeen | lazy | extra[pykeen] |
| latence-linkpred-ultra | torch |
torch | lazy | extra[ultra], extra[pykeen] |
| latence-linkpred-ultra | torch_geometric |
torch-geometric | lazy | not a plain PyPI pin — PyG wheel index, note 2 |
| latence-ner-endpoint | openai |
openai | lazy | declared |
| latence-ner-gliner | gliner |
gliner | lazy | declared |
| latence-parser-document | pypdf |
pypdf | lazy | declared |
| latence-parser-endpoint | openai |
openai | lazy | declared |
| latence-parser-glm | PIL |
pillow | lazy | declared |
| latence-parser-glm | pypdfium2 |
pypdfium2 | lazy | declared |
| latence-parser-glm | transformers |
transformers | lazy | declared |
| latence-parser-lighton | PIL |
pillow | lazy | declared |
| latence-parser-lighton | pypdfium2 |
pypdfium2 | lazy | declared |
| latence-parser-lighton | transformers |
transformers | lazy | declared |
| latence-parser-lighton-vllm | openai |
openai | lazy | declared (+ latence-parser-lighton) |
| latence-parser-pdfplumber | pdfplumber |
pdfplumber | lazy | declared |
| latence-parser-plaintext | (none) | — | — | pure latence-core consumer |
| latence-parser-render | PIL |
pillow | lazy | declared |
| latence-parser-render | docx |
python-docx | lazy | declared |
| latence-parser-render | openpyxl |
openpyxl | lazy | declared |
| latence-parser-render | pptx |
python-pptx | lazy | declared |
| latence-parser-render | pypdfium2 |
pypdfium2 | lazy | declared |
| latence-pii-gliner | gliner |
gliner | lazy | declared |
| latence-pii-gliner2 | gliner2 |
gliner2 | lazy | declared (+ peft runtime dep, note 3) |
| latence-pii-presidio | presidio_analyzer |
presidio-analyzer | lazy | declared (spacy is a presidio transitive dep) |
| latence-relation-gliner | gliner |
gliner | lazy | declared |
| latence-relation-llm | openai |
openai | lazy | declared |
| latence-runner-airflow | airflow |
apache-airflow | no (lazy) | extra[airflow], note 2 |
| latence-schema-inducer | openai |
openai | lazy | declared |
Notes¶
-
latence-core→torch(lazy, undeclared by design). Imported only inside functions inproviders/perf.py(dtype materialisation,torch.compile) andtune/fingerprint.py(version read), each with anoqa: PLC0415"optional heavy dep" marker and a graceful fallback. Core is the near-zero-dep spine (ADR-0016); declaringtorchwould defeat that. Not a gap. -
Deliberately-non-PyPI heavy deps.
latence-runner-airflowguardsfrom airflow …insidebuild_dag()with anImportErrorpointing at the[airflow]extra (apache-airflow>=2.7).latence-linkpred-ultraguards the whole ULTRA/PyKEEN stack;torch-geometric+torch-scatterare intentionally not declarable as plain PyPI pins — they must be installed from the PyG wheel index matched to the installed torch/CUDA (torch-scatterhas no universal wheel and fails to build without torch present). The package's[project.optional-dependencies]comment documents the exact-f https://data.pyg.org/whl/…install line. Not a gap. -
peft(already fixed, the reference case).gliner2's relation-inference engine (gliner2.inference.engine → gliner2.training.trainer) importspeftat runtime, butgliner2[local]does not declare it — a real load failsModuleNotFoundError: peft.peft>=0.10is declared on bothlatence-extract-gliner2andlatence-pii-gliner2(commitcdbb8c2). This is the hidden-transitive-import class — invisible to a src import scan, only surfaced by loading real weights on the pod. No other package pulls a heavy dep with an equivalently-incomplete extra:gliner(v1),openai,sentence-transformers,presidio-analyzer,pypdf,pdfplumber,pypdfium2,transformers,pillow,python-docx,python-pptx,openpyxlall declare their own transitive runtime deps correctly via PyPI metadata. -
License flag —
easydictis LGPL-3.0.easydict(pulled only by the opt-inlatence-linkpred-ultra[ultra]extra, used by the vendored MIT ULTRA inference subset) is LGPL-3.0 on PyPI, not permissive in the MIT/BSD/Apache class. LGPL-3.0 is weak copyleft: dynamic import by permissively-licensed code (as here — pure-Python, no modification of easydict) is permitted without relicensing the framework, and the extra is opt-in and off the default install path. Flagged per ADR-0012 / research-diligence so the maintainer decides consciously (options: keep behind the opt-in extra as-is with this note, or replace easydict's trivial dotted-dict usage in the vendored ULTRA config with a permissive shim). The other ULTRA/PyKEEN extras are permissive:torchBSD-3-Clause,torch-geometricMIT,torch-scatterMIT,pykeenMIT (verified 2026-07-10).