Plugins for the PDF-TEI-Editor. Each plugin in plugins/<name>/ is a Python package (pte-plugin-<name>, module pte_plugin_<name>) that the editor discovers through the pte.plugins entry point once it is installed into the editor's environment.
| Plugin | Package | Version | Description |
|---|---|---|---|
| annotation-history | pte-plugin-annotation-history |
1.0.0 | Shows detailed annotation history for PDF documents |
| annotation-progress | pte-plugin-annotation-progress |
1.0.0 | View annotation progress in the current collection |
| annotation-review | pte-plugin-annotation-review |
1.0.0 | Reviews a document's annotations against its own editorialDecl-linked rules using an LLM |
| anthropic-llm | pte-plugin-anthropic-llm |
1.0.0 | Registers Anthropic's Claude API as an LLM inference provider |
| archival-edition | pte-plugin-archival-edition |
0.1.0 | Extractor for photographed archival documents with a description form, schema and annotation guidelines |
| collection-overview | pte-plugin-collection-overview |
1.0.0 | Overview of gold-standard coverage and annotation progress across all accessible collections |
| edit-history | pte-plugin-edit-history |
1.0.0 | View recent activity in the current collection |
| google-llm | pte-plugin-google-llm |
1.0.0 | Registers Google's Gemini API as an LLM inference provider |
| grobid | pte-plugin-grobid |
1.0.0 | Extract training data using GROBID server |
| iaa-analyzer | pte-plugin-iaa-analyzer |
1.0.0 | Compute agreement between annotation versions by comparing element positions in |
| inference-endpoints | pte-plugin-inference-endpoints |
1.0.0 | Registers OpenAI-compatible inference endpoints (e.g. Ollama) as LLM providers |
| kisski | pte-plugin-kisski |
2.0.0 | Text and PDF processing using KISSKI Academic Cloud API |
| llamore | pte-plugin-llamore-extractor |
1.0.0 | Extract references using LLamore with the configured LLM model |
| local-sync | pte-plugin-local-sync |
1.0.0 | Synchronize collection documents with local filesystem |
| metadata-extraction | pte-plugin-metadata-extraction |
1.1.0 | Extract bibliographic metadata from PDFs, enrich TEI headers and backfill missing metadata in collections |
| revision-feed | pte-plugin-revision-feed |
1.0.0 | Per-project Atom feeds of document revisions |
| rng-converter | pte-plugin-rng-converter |
1.0.0 | Generate RelaxNG schema from TEI document structure |
| update-metadata | pte-plugin-update-metadata |
1.0.0 | Update TEI documents with complete bibliographic metadata from DOI lookup |
| webdav-sync | pte-plugin-webdav-sync |
1.0.0 | Synchronize files with a WebDAV server |
pte-plugins.json lists the same information in machine-readable form. It is generated from the packages by python scripts/build_index.py; CI checks that it is up to date.
The overview page on GitHub Pages lists the plugins with their metadata and links to their READMEs. The Pages workflow rebuilds it with python scripts/build_site.py on every push to main. The site also publishes pte-plugins.json and each plugin's docs/ directory at https://mpilhlt.github.io/pte-plugins/<name>/ (e.g. the archival-edition schema and guidelines).
archived/ contains plugins that are no longer maintained. They are not packaged and not tested.
The packages are installed into the Python environment of the editor. From a checkout of the editor:
uv pip install "pte-plugin-grobid @ git+https://github.com/mpilhlt/pte-plugins#subdirectory=plugins/grobid"Plugins that depend on other plugins of this repository (see the "requires" field of the index) declare them as package dependencies; uv resolves them within this repository's workspace. Some plugins need configuration (server URLs, API keys) before they become available; see their READMEs.
If the operator enabled runtime install (PTE_PLUGIN_INSTALL=1), administrators can also install the released plugins in the editor's plugin manager ("Available" tab). This works for plugins whose Python dependencies (the "dependencies" field of the index) are already installed in the editor's environment; other plugins must be installed as above.
Releases are what the plugin manager installs, and they are triggered by commit messages, not by hand. A commit releases a plugin only when its Conventional Commits scope is exactly that plugin's id (the id field in pte-plugins.json, e.g. grobid or archival-edition):
fix(grobid): handle a broken bibl split -> patch release of grobid
feat(archival-edition): support TEI facsimile -> minor release of archival-edition
A breaking change needs a BREAKING CHANGE: footer (not the type(scope)!: shorthand, which this project's parser configuration does not recognize) to trigger a major release:
fix(grobid): rename the public extractor function
BREAKING CHANGE: `extract()` is now `extractBibl()`
Commits without a scope, or whose scope does not match any plugin id, release nothing. A commit scoped to one plugin is never counted for another, even one whose id is a prefix of it (grobid vs. a hypothetical grobid-extra).
On every push to main, .github/workflows/semantic-release.yml runs semantic-release once per plugin listed in pte-plugins.json, scoped to that plugin's own commits since its last release (see release.config.js and scripts/release/scoped-release-plugin.js). For a plugin with a qualifying commit, it: raises version in the plugin's pte-plugin.json (via scripts/bump_plugin_version.py), regenerates pte-plugins.json, commits both as chore(release): <plugin-id> <version>, and tags the commit <plugin-id>-v<version>. That tag is what the release workflow listens for: it checks that the tag matches the manifest version, builds the wheel, and creates a GitHub release with the wheel, a SHA256SUMS file and a build provenance attestation. The plugin manager installs only wheels whose sha256 matches SHA256SUMS.
A manual pte-plugin.json version bump without a matching commit is not recommended: the next automated release recomputes the version from commit history and will overwrite it.
plugins/<name>/
├── pyproject.toml # package metadata, Python dependencies, pte.plugins entry point
├── README.md
└── src/pte_plugin_<name>/
├── pte-plugin.json # version and compatible editor/plugin API versions
├── plugin.py # Plugin subclass
├── ...
└── tests/ # not part of the wheel
├── test_*.py # unit tests (pytest)
├── *.test.js # JavaScript unit tests (node --test)
├── api/*.test.js # API tests, run by the editor's backend test runner
├── e2e/*.spec.js # Playwright tests, run by the editor's E2E runner
└── integration/ # tests that need external services, run manually
pte-plugin.json is the single source of the version: the package version is read from it ([tool.hatch.version]), and the editor shows it in the plugin manager. engines.pte is the range of editor versions and engines.pte-plugin-api the range of plugin API versions (fastapi_app.plugin_api) a plugin works with; the editor does not load plugins outside these ranges. See the editor's backend plugin guide for the plugin API.
The tests run in the environment of an editor checkout, by default ../pdf-tei-editor next to this repository (set PTE_ROOT otherwise). Install all plugins into it as editable packages:
export PTE_ROOT=$(realpath ../pdf-tei-editor)
uv pip install --python "$PTE_ROOT/.venv/bin/python" $(for d in plugins/*/; do echo "-e $d"; done)As long as the editor still ships built-in copies of these plugins in fastapi_app/plugins/, remove them from that checkout first, since a plugin id must not be loaded twice. uv sync in the editor removes the editable installs again; uv run keeps them (set UV_NO_SYNC=1 to be sure).
Run the tests:
"$PTE_ROOT/.venv/bin/python" -m pytest # Python unit tests of all plugins, plus tests/
npm install && npm test # JavaScript unit tests
# API and E2E tests: the editor's runners stage the plugins' tests/api and tests/e2e files
cd "$PTE_ROOT"
node tests/backend-test-runner.js --test-dir tests/api/v1 --plugin-tests ../pte-plugins
node tests/e2e-runner.js --plugin-tests ../pte-pluginsJavaScript tests that need the editor's test helpers import them through scripts/core.js.
CI (.github/workflows/tests.yml) runs all of this against the editor's devel branch.
- Create
plugins/<name>/with the layout above. Copypyproject.tomlfrom an existing plugin and adapt name, description, dependencies and the entry point (<plugin-id> = "pte_plugin_<name>"). - Import editor functionality from
fastapi_app.plugin_apiwhere possible; other core modules may change without notice. - Run
python scripts/build_index.pyand add the plugin to the table above.