Skip to main content

Anchor-addressed DOCX editing for LLM agents — a thin client over Docxodus' DocxSession.

Project description

docx-scalpel

Anchor-addressed DOCX editing for LLM agents — a thin client over Docxodus' DocxSession.

docx-scalpel exposes Docxodus' stateful DOCX editor over a long-running .NET subprocess (docxodus-pyhost). The session lives in the host's memory until you explicitly release it, so an LLM agent can issue dozens of small edits against one document without paying the OOXML parse + Unid annotation + projection cost on every call.

Status: Alpha. linux-x64 wheels ship with a bundled docxodus-pyhost; other RIDs require a dev clone of Docxodus until the wheel matrix is extended (tracked in RELEASING.md).

Installation

pip install docx-scalpel

linux-x64 today; pre-release tags ship as 0.1.0a*, so use --pre if you want to opt in:

pip install --pre docx-scalpel

Source installs (pip install of the sdist, or pip install -e . from a dev clone) don't include a bundled host. Set DOCXODUS_HOST=/path/to/docxodus-pyhost to point at one you built, or run dotnet build tools/python-host/pyhost.csproj inside a Docxodus monorepo clone — the locator auto-discovers it.

Quick start

from docx_scalpel import open_session, FormatOp, Position

with open("contract.docx", "rb") as f:
    docx_bytes = f.read()

with open_session(docx_bytes) as session:
    # Walk template placeholders and fill them. The picker returns a string to
    # replace, or None to skip. fill_placeholders handles reverse-offset
    # ordering, $-prefix preservation, and multi-pass nested-bracket convergence
    # in one call.
    result = session.fill_placeholders(lambda p: "filled value")
    print(f"filled {result.filled} placeholders in {result.passes} passes")

    # Add a heading after the first body paragraph.
    proj = session.project()
    first_p = next(
        t for t in proj.anchor_index.values()
        if t.kind in ("p", "h") and t.scope == "body"
    )
    session.insert_paragraph(first_p.id, Position.AFTER, "## Reviewed by counsel")

    # Bold the first 8 characters of that paragraph.
    session.apply_format_by_substring(first_p.id, "Reviewed", FormatOp(bold=True))

    new_bytes = session.save()

with open("filled.docx", "wb") as f:
    f.write(new_bytes)

The with block is the documented lifecycle path — it calls session.close() on the way out, which releases the session from the host's SessionRegistry. A __del__ finalizer is a fallback for forgotten sessions but should not be relied on; interpreter shutdown may skip it.

Why a subprocess?

DocxSession holds a parsed WordprocessingDocument, an AnchorIndex of Unid-stamped block-level targets, a cached MarkdownProjection, and a bounded UndoRing of per-part XDocument snapshots. Recreating it costs tens of ms on small docs and seconds on large ones. The subprocess model lets one Python process drive many sessions across many calls, all in one host's memory, until you decide to close them.

Architecture:

Python process                 docxodus-pyhost (.NET 8)
─────────────                  ──────────────────────────
DocxSession  ──NDJSON──>       Dispatcher
                               │
                               ▼
                               DocxSessionOps
                               │
                               ▼
                               SessionRegistry (handle → DocxSession)

One host per Python process. Many sessions inside the host. atexit sends shutdown and (if the host doesn't comply) terminates / kills.

Full design + wire-protocol spec: docs/architecture/python_docxodus.md. Delta-spec for the docx-scalpel rebrand: docs/superpowers/specs/2026-05-26-docx-scalpel-design.md.

Development

Build the host binary (one-time)

# From the Docxodus repo root:
dotnet build tools/python-host/pyhost.csproj -c Release

This produces tools/python-host/bin/Release/net8.0/docxodus-pyhost. _host_locator.py discovers it automatically when you pip install -e . from a monorepo clone.

For non-monorepo development, set DOCXODUS_HOST=/path/to/docxodus-pyhost to override the discovery path.

Editable install + tests

cd python
python -m venv .venv
.venv/bin/pip install -e .[test]
.venv/bin/pytest -v

Test layout

  • tests/test_smoke.py — end-to-end mirror of Docxodus.Tests/DocxSessionSmokeTest.cs. v1 acceptance gate.
  • tests/test_lifecycle.py — proves session persistence, idempotent close, singleton host, finalizer fallback.

Tests share the Docxodus monorepo's TestFiles/ corpus so divergence between Python and .NET on identical inputs is detectable.

API surface

The DocxSession class exposes every op in Docxodus.Internal.DocxSessionOps as a snake-case method:

Tier Methods
Lifecycle save, close, undo, redo
Projection project, project_anchor
Discovery grep, grep_cross_block, find_placeholders, find_by_text, find_all_by_text, find_by_regex, find_by_kind, find_by_annotation, find_by_label, find_by_bookmark, list_annotations, exists, get_anchor_info, get_anchor_infos, get_edit_summary, remaining_placeholders, get_diff
A: text mutations replace_text, replace_text_range, replace_text_at_span, replace_inner, replace_match, delete_block, delete_range, delete_section
B: structural insert_paragraph, split_paragraph, merge_paragraphs
C: formatting apply_format, apply_format_by_substring, set_paragraph_style, set_list_level, remove_list_membership
D: tables replace_cell_content
Raw XML session.raw.get_xml, session.raw.insert_xml, session.raw.replace_xml

Every mutation method returns an EditResult envelope — transport-level failures raise DocxodusTransportError, but a business outcome (anchor_not_found, malformed_markdown, etc.) returns EditResult(success=False, error=EditError(...)). Never an exception across the API boundary.

License

MIT. Built on top of Docxodus, which is itself a fork of Open-Xml-PowerTools.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

docx_scalpel-0.1.0a4.tar.gz (32.0 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

docx_scalpel-0.1.0a4-py3-none-win_amd64.whl (47.8 MB view details)

Uploaded Python 3Windows x86-64

docx_scalpel-0.1.0a4-py3-none-manylinux_2_28_x86_64.whl (47.8 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ x86-64

docx_scalpel-0.1.0a4-py3-none-manylinux_2_28_aarch64.whl (44.7 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ ARM64

docx_scalpel-0.1.0a4-py3-none-macosx_11_0_arm64.whl (41.0 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

File details

Details for the file docx_scalpel-0.1.0a4.tar.gz.

File metadata

  • Download URL: docx_scalpel-0.1.0a4.tar.gz
  • Upload date:
  • Size: 32.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for docx_scalpel-0.1.0a4.tar.gz
Algorithm Hash digest
SHA256 193a16dcb2869fa08140ba267792fdcfeea8c242e9dcaadaae43153919e095d4
MD5 344515c2ee88050e214b582b79701eb3
BLAKE2b-256 7e04f0fc9e9daa74a66b2022b6f69661c7902d7d04941cb533a3a414976c3595

See more details on using hashes here.

Provenance

The following attestation bundles were made for docx_scalpel-0.1.0a4.tar.gz:

Publisher: python-publish.yml on JSv4/Docxodus

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file docx_scalpel-0.1.0a4-py3-none-win_amd64.whl.

File metadata

File hashes

Hashes for docx_scalpel-0.1.0a4-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 9ca1098fdc10a02deb07158f0c5ba9c87bd291f57e20a598f586c4a2254b97d7
MD5 f27a044a0d8e1d2b5c1b765e4d8ee88b
BLAKE2b-256 5f88aa20b2f26b07a9193216a2b35e87b507069d59a8b33f7f9b0504be9497ff

See more details on using hashes here.

Provenance

The following attestation bundles were made for docx_scalpel-0.1.0a4-py3-none-win_amd64.whl:

Publisher: python-publish.yml on JSv4/Docxodus

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file docx_scalpel-0.1.0a4-py3-none-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for docx_scalpel-0.1.0a4-py3-none-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 28f4eb0134c6966154907ef87e9b7520199bfa4f1c5e7eecd0a688b3af327e74
MD5 c75d26913a72d0efe10789b8ac764a02
BLAKE2b-256 ab4f899e1be766961b3cc16bcb58e99b5626951392fcb09641e566307647b6ae

See more details on using hashes here.

Provenance

The following attestation bundles were made for docx_scalpel-0.1.0a4-py3-none-manylinux_2_28_x86_64.whl:

Publisher: python-publish.yml on JSv4/Docxodus

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file docx_scalpel-0.1.0a4-py3-none-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for docx_scalpel-0.1.0a4-py3-none-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 eba7b9ac7bdd2d76f79ca486bd15aca4e97d852788274e0e6b3aed7b8f37822a
MD5 9e5325fcc2c4418f5b0b164264b2369b
BLAKE2b-256 d5b36b35da2cea9c637e36a586b0c9cf1b2b0e9c39fec31f451364cbc0d3f22b

See more details on using hashes here.

Provenance

The following attestation bundles were made for docx_scalpel-0.1.0a4-py3-none-manylinux_2_28_aarch64.whl:

Publisher: python-publish.yml on JSv4/Docxodus

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file docx_scalpel-0.1.0a4-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for docx_scalpel-0.1.0a4-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 4f649f2012e098bd0a891facc53e16997bc0100eebf34187c668ed8286e28ecb
MD5 27a082cff252d8236bc97ff8b8d2f65c
BLAKE2b-256 cfcaf16f2c3b3b83151797cbb487b4f3cfa2ab7536717b1076930b533279ebd2

See more details on using hashes here.

Provenance

The following attestation bundles were made for docx_scalpel-0.1.0a4-py3-none-macosx_11_0_arm64.whl:

Publisher: python-publish.yml on JSv4/Docxodus

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page