Skip to main content

edit2docs

AI-agent-native document engine — DOCX · XLSX · PPTX. English-first, with first-class Korean support.

PyPI Python License: Apache-2.0

한국어 README

edit2docs generates complete Office documents from a one-line intent and chat-edits existing files — Word reports, Excel workbooks, PowerPoint decks — always producing natively editable OOXML (real paragraphs, real cells, real charts — never screenshots of them). One engine, four surfaces: import it, hand it to an agent, plug it into an MCP client, or run it as a service.

pip install edit2docs              # library + agent tools + local MCP
pip install "edit2docs[server]"    # + the hosted multi-tenant service
from edit2docs import generate_doc, edit_doc

generate_doc("Executive briefing on Q3 sales", output="deck.pptx")
r = edit_doc("deck.pptx", "Make slide 3's title more assertive")
print(r.reply)          # the editor explains what it changed

Ecosystem

Repo What it is
edit2docs (this repo) The engine: library · agent tools · MCP · hosted FastAPI service
edit2docs-web Web studio for the hosted service — upload, generate, chat-edit with a live addressable preview, per-op edit highlighting, EN/KO UI. Next.js 15 / React 19 / Tailwind
ppt-master Upstream project (MIT) the PPTX core is derived from — synced through v3.1
edit2ppt Sister project; the deck pipeline and hosted service originate there

A production deployment of engine + studio runs behind hr_blog2.0's compose stack — its edit2docs-server/ and edit2docs-web/ service dirs are a working reference for wiring both containers behind nginx.


The seven verbs

Every surface exposes the same seven format-dispatched verbs — the file extension picks the engine. Deterministic verbs need no API key; generative ones are BYOK (api_key=... or ANTHROPIC_API_KEY).

verb what it does LLM?
generate_doc intent (+ optional sources / PPTX template) → complete document
edit_doc one natural-language edit turn; untouched content stays byte-identical
preview_doc .pptx → per-slide SVG · .docx/.xlsx → markdown
render_doc any format → page PNGs / PDF / SVGs — no LibreOffice, no subprocess
analyze_doc structure outline with the exact addresses set_doc_text / edit_chart need (incl. a charts list)
set_doc_text deterministic targeted edits — lossless (charts / images / styles / formulas survive)
edit_chart deterministically edit a native chart's data or title — rewrites the chart and its embedded workbook

Lossless editing. The deterministic edit verbs run on contextifier's raw OOXML layer: edits are surgical and untouched package parts stay byte-identical, so a chart, pivot table, sparkline, inline image or cached formula is never collateral damage of a text edit. The PPTX chat editor (edit_doc on a deck) likewise preserves native charts and tables on slides it regenerates, instead of flattening them into pictures.


1 · Python library

Generate — the output extension picks the engine

from edit2docs import generate_doc

generate_doc("Q3 performance report", output="report.docx")
generate_doc("Quarterly sales summary", output="sales.xlsx", sources=["raw.pdf"])
generate_doc("Executive briefing on Q3 sales", output="deck.pptx",
             template="brand.pptx",          # optional user PPTX template
             deck_mode="template_restyle",   # "new" | "template_restyle" | "template_extend"
             pages=(8, 12))                  # target page range (pptx)
generate_doc("3분기 실적 보고서", output="report.docx", lang="ko-KR")  # any language, same call

Full signature: generate_doc(intent, *, output, api_key=None, sources=None, template=None, deck_mode="new", pages=(8, 12), lang="en-US", model=...)GenerateResult(path, page_count, design_spec, warnings).

sources accepts PDF / DOCX / DOC / PPTX / XLSX / HTML / EPUB / IPYNB paths — each is converted to markdown and given to the writer as reference material.

Edit — one chat turn, everything else byte-identical

from edit2docs import edit_doc

r = edit_doc("report.docx", "Add a 'deployment complete' item to the progress section")
print(r.reply)        # what the editor did, in your language
print(r.operations)   # the applied ops, e.g. [{"action": "insert_after", ...}]

r = edit_doc("deck.pptx", "이 문서 내용을 반영해서 3번 슬라이드를 고쳐줘",
             sources=["notes.pdf"], lang="ko-KR",
             chat_history=[{"role": "user", "content": "..."},
                           {"role": "assistant", "content": "..."}])

The planner sees a numbered outline of your document, plans the minimal operations, and the deterministic engine applies them — untouched paragraphs, cells and slides survive byte-for-byte. If planning fails, the reply says so honestly instead of pretending (no silent no-ops).

Inspect & edit deterministically (no LLM, no key)

from edit2docs import analyze_doc, set_doc_text, preview_doc, render_doc

info = analyze_doc("report.docx")
# {"format": "docx", "outline": [
#    {"para": 0, "style": "Heading 1", "text": "Q3 Report"},
#    {"table": 0, "row": 1, "col": 2, "text": "142"}, ...]}   ← addresses

set_doc_text("report.docx", [
    {"para": 0, "new_text": "Q3 Final Report"},               # docx: replace / insert_after / delete
])
set_doc_text("sales.xlsx", [
    {"sheet": "Sales", "cell": "B3", "value": 142},           # xlsx: set_cell / append_rows / add_sheet
])
set_doc_text("deck.pptx", [
    {"slide": 0, "shape_id": 2, "para": 0, "new_text": "New title"},  # pptx
])

# Edit a native chart's data or title — the chart stays a real, editable
# PowerPoint/Excel chart (its embedded workbook is rewritten too).
from edit2docs import edit_chart, list_charts

list_charts("deck.pptx")   # [{"chart": 0, "kind": "bar", "title": ..., "series": [...]}]
edit_chart("deck.pptx", [
    {"chart": 0, "title": "Q3 Sales"},
    {"chart": 0, "categories": ["Q1", "Q2", "Q3"],
     "series": [{"name": "Sales", "values": [120, 135, 150]}]},
])

preview_doc("deck.pptx", out_dir="previews")   # per-slide self-contained SVGs
render_doc("report.docx", to="pdf")            # page PNGs / a PDF / raw SVGs
render_doc("deck.pptx", to="png", dpi=200)     # resvg raster — no LibreOffice

Async variants exist for the generative verbs: async_generate_doc, async_edit_doc (use inside an existing event loop).


2 · Agent tools (function calling)

The same seven verbs as Anthropic tool-use schemas plus a dispatcher:

import anthropic
from edit2docs.agent_tools import ANTHROPIC_TOOLS, run_tool

client = anthropic.Anthropic()
msg = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=2048,
    tools=ANTHROPIC_TOOLS,
    messages=[{"role": "user", "content": "Fix the title of slide 3 in deck.pptx"}],
)
for block in msg.content:
    if block.type == "tool_use":
        result = run_tool(block.name, block.input)   # sync; run_tool_async also exists

3 · Local MCP server (zero infra)

pip install edit2docs ships an edit2docs-mcp stdio server exposing all six verbs over local files:

// Claude Desktop / Claude Code / Cursor
{
  "mcpServers": {
    "edit2docs": {
      "command": "edit2docs-mcp",
      "env": { "ANTHROPIC_API_KEY": "sk-ant-..." }   // only generative tools need it
    }
  }
}

Then just talk: "generate a 10-page deck about our roadmap as ~/decks/roadmap.pptx, then render it to PDF".


4 · Hosted service

pip install "edit2docs[server]"
edit2docs serve                    # FastAPI on :8000 — standalone mode

Standalone mode needs zero external infra: SQLite + local-fs storage + an inline job queue, auto-bootstrapped on first start. Add Postgres / Redis / S3 via env vars when you outgrow it.

REST endpoint purpose
POST /v1/assets · GET /v1/assets/{id} upload / fetch documents (200 MB cap)
POST /v1/jobs/generate-deck · /v1/jobs/edit-deck queue generative jobs (any of the 3 formats)
GET /v1/jobs/{id} · GET /v1/jobs/{id}/events job status · SSE progress stream (stages + per-operation live-edit events with addressable targets)
POST /v1/preview pptx → per-slide SVGs · docx/xlsx → addressable HTML
POST /v1/text-edits deterministic targeted edits
GET /health liveness + mode report
/mcp · /mcp-sse the same verbs over MCP (Streamable HTTP / SSE)

Anthropic keys are BYOK per request (X-Anthropic-API-Key header) — never persisted. Errors come back bilingual: message follows the request's Accept-Language, with message_en / message_ko always present.

Key env vars (prefix EDIT2DOCS_):

var default notes
EDIT2DOCS_DEFAULT_LANG en-US set ko-KR to make a deployment Korean-by-default
EDIT2DOCS_DATA_DIR /data/edit2docs standalone SQLite + file storage root
EDIT2DOCS_DATABASE_URL (sqlite) e.g. postgresql+asyncpg://...
EDIT2DOCS_REDIS_URL (inline queue) enables the arq worker queue
EDIT2DOCS_S3_* (local fs) endpoint / bucket / keys for S3-compatible storage
EDIT2DOCS_AUTH_DEV_API_KEY (anonymous) single bearer token for small deployments
EDIT2DOCS_MAX_UPLOAD_SIZE_BYTES 200 MB match your reverse proxy

The web studio

edit2docs-web is the official frontend for this service: drag-and-drop upload, generation with staged SSE progress, and a co-editing studio where the chat edits your document while the canvas highlights the exact paragraph / cell / slide each operation touches (the preview HTML carries data-e2d-* addresses; PPTX slides carry data-e2p-*). English-first UI with a KO/EN toggle. Point it at the engine with EDIT2DOCS_SERVER_INTERNAL_URL + EDIT2DOCS_SERVER_API_KEY.


How each format works

  • DOCX — the writer LLM emits a constrained markdown document; a deterministic renderer (python-docx) turns it into styled Word. Edits are paragraph-addressed operations (replace / insert_after / delete, table cells by table/row/col). The hosted preview is a native, addressable HTML rendering — every paragraph carries data-e2d-para, every cell data-e2d-cell, the same addresses the outline and the live-edit op stream use — with real merged cells, alignment, colors, images, footnotes and page breaks.
  • XLSX — the designer LLM emits a YAML sheet spec (sheets / headers / rows / number formats, formulas allowed); openpyxl renders styled sheets. Edits are set_cell / append_rows / add_sheet with staleness guards. The hosted preview is a spreadsheet-style grid (column letters, row numbers, merged ranges, cached formula results), every cell stamped data-e2d-cell="B3" — exactly the address set_cell takes.
  • PPTX — the full multi-stage pipeline: strategist → per-page SVG → native DrawingML, user-PPTX templates (restyle / extend), chat-edit with slide recompose, per-paragraph text edits incl. table cells, optional Edge-TTS narration. Exported text is paragraph-merged (edits as real paragraphs, not per-line boxes).

Native charts & tables (PPTX)

SVG groups marked data-pptx-native="chart|table" export as real, editable PowerPoint objects — a chart XML part with an embedded Excel workbook (double-click it in PowerPoint and edit the data), or a native <a:tbl> table — instead of drawn shapes:

<g id="sales_chart" data-pptx-native="chart">
  <metadata data-pptx-native="chart">
    { "name": "sales_chart",
      "x": 125, "y": 141, "width": 1000, "height": 440,
      "type": "bar",
      "categories": ["Q1", "Q2", "Q3"],
      "series": [{ "name": "Sales", "values": [120, 135, 150] }] }
  </metadata>
  <!-- fallback shapes, used when native export is off -->
</g>

Opt in with ExportRequest(native_objects=True) (tools/export.py). Supported chart types: bar / column / line / area / pie / doughnut / of-pie / radar (classic), scatter / bubble (XY), and box-whisker / funnel / histogram / pareto / sunburst / treemap / waterfall (chartEx). The quality checker validates marker payloads before export.

Every LLM planner follows the same contract: fenced reply + edit_plan blocks, one retry with a format reminder, and an honest reply (instead of a silent no-op) when planning fails.


Languages

English is the default (lang="en-US"); Korean is a first-class citizen, not an afterthought — Hangul-aware text widths, per-run OOXML lang attributes detected from the actual script, Korean font stacks (Pretendard / Malgun), a complete Korean message catalog, localized chat replies and live-edit labels. Flip any call with lang="ko-KR", any request with Accept-Language: ko-KR, or a whole deployment with EDIT2DOCS_DEFAULT_LANG=ko-KR. zh-CN / zh-TW / ja-JP get the same script detection and font-stack treatment.


Development

git clone https://github.com/CocoRoF/edit2docs && cd edit2docs
uv venv .venv && uv pip install -e ".[server,dev]"
.venv/bin/python -m pytest tests/          # 769 tests
.venv/bin/python -m ruff check src/edit2docs --exclude src/edit2docs/core

Version history

version highlights
v0.8.0 lossless editing on contextifier's raw OOXML layer — set_doc_text/edit_doc no longer destroy charts, images, sparklines, styles or cached formulas; PPTX chat-edit preserves native charts/tables; new edit_chart verb (data + title, embedded workbook synced)
v0.7.0 upstream sync (ppt-master v2.7 → v3.1, 3 waves): native chart/table export, paragraph-merge editability, PowerPoint repair-prompt fixes, checker hardening · English-first flip with full Korean support
v0.5–0.6 render_doc — native page rendering to PNG/PDF/SVG for all 3 formats (resvg + PyMuPDF, no LibreOffice)
v0.4.0 addressable native previews (data-e2d-*) — preview, outline and editor share one address space
v0.3.0 live edit streaming — per-operation SSE events with addressable targets
v0.2.x multi-format hosted API + full-format hardening
v0.1.0 multi-format engine: the core verbs across DOCX/XLSX/PPTX

License

Apache-2.0. The PPTX core under src/edit2docs/core/ is derived from ppt-master (MIT, © Hugo He) via edit2ppt and kept in sync (currently through upstream v3.1); the original MIT terms for those portions are preserved in NOTICE and LICENSE.ppt-master.MIT.

Download files

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

Source Distribution

edit2docs-0.8.0.tar.gz (5.9 MB view details)

Uploaded Source

Built Distribution

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

edit2docs-0.8.0-py3-none-any.whl (10.8 MB view details)

Uploaded Python 3

File details

Details for the file edit2docs-0.8.0.tar.gz.

File metadata

  • Download URL: edit2docs-0.8.0.tar.gz
  • Upload date:
  • Size: 5.9 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for edit2docs-0.8.0.tar.gz
Algorithm Hash digest
SHA256 cade5eb2d9b85557d18c032e083f2557dfbc649d54b9c417b51e16461365fffd
MD5 46e5b8056ba29e8a5c4175bb17517601
BLAKE2b-256 2321485d56dd2ba6ec31debd1c285a4471b01f4369b295c38d2ef868e28fc95f

See more details on using hashes here.

Provenance

The following attestation bundles were made for edit2docs-0.8.0.tar.gz:

Publisher: publish.yml on CocoRoF/edit2docs

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

File details

Details for the file edit2docs-0.8.0-py3-none-any.whl.

File metadata

  • Download URL: edit2docs-0.8.0-py3-none-any.whl
  • Upload date:
  • Size: 10.8 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for edit2docs-0.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 517732f5e4e09d29eb3c852482e8bf62a884554868cba73cc078d1bea892b3c1
MD5 aebac1f1da939d782143ee71571246f4
BLAKE2b-256 ad6ce021ff1a473e72859403ed299add3d4636028be1ee80fe95181d0e66ced8

See more details on using hashes here.

Provenance

The following attestation bundles were made for edit2docs-0.8.0-py3-none-any.whl:

Publisher: publish.yml on CocoRoF/edit2docs

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