This document describes packaging the Python venv compute bridge and its scientific productivity surfaces as a stable core extension (LibrePy), while WriterAgent keeps the fast-moving AI features (chat, tools, MCP, grammar, embeddings, and so on).
It answers two questions:
- Which
plugin/framework/modules must ship (entire files, even when only one function is used)? - What is the complete file list for the feature bundle (same whole-file rule)?
For user-facing behavior (=PYTHON() / =PY(), Run Python Script, domain helpers, Monaco, OCR, TeX), see Enabling NumPy & Python in LibreOffice. This doc is about packaging and dependencies, not tutorials.
Build / install targets and the prototype tree live under Prototype extension.
LibrePy.oxt is a working standalone extension in this repo — not just a paper design.
| Item | Status |
|---|---|
make build-core → build/LibrePy.oxt |
Shipped — scripts/build_librepy_oxt.py + scripts/librepy_bundle_paths.py |
make deploy-core / register-librepy-oxt |
Shipped — installs org.extension.librepy; removes WriterAgent (org.extension.writeragent) so only one OXT is active |
| Extension identity | Shipped — extension-core/ (description.xml, Addons.xcu, Jobs.xcu, ProtocolHandler.xcu, CalcAddIns, XPythonFunction.rdb) |
| Bootstrap | Shipped — plugin/main_core.py, Python-only Settings (plugin/librepy/settings.py) |
| Layers 0–6 feature set | Shipped in LibrePy.oxt — =PY() / =PYTHON(), warm venv, Run Python Script, Reset Session, Monaco, domain helpers, Vision/OCR, TeX/Math (see Feature bundles); user guide: enabling_numpy_in_libreoffice.md |
| Filtered locales / slim vendor | Shipped — make compile-translations-core; vendor limited to json_repair + latex2mathml |
xl Calc-parity helpers |
Deferred — excluded from LibrePy (§ below) |
WriterAgent AI overlay on top of LibrePy (both OXTs installed, shared plugin/ via extend_path) |
Not shipped — goal in Coexistence / prototype §7; today install only one OXT at a time |
WriterAgent stripped of duplicate =PY() / menus |
Not shipped — full WriterAgent OXT still bundles its own Python stack |
plugin/__init__.py already uses pkgutil.extend_path (useful for other dual-OXT pairs such as WriterAgent + LibreHarper). LibrePy × WriterAgent side-by-side ownership of =PY() is still future work.
Aligned with enabling_numpy_in_libreoffice.md and linked topic docs.
| In core extension | WriterAgent extension only |
|---|---|
=PYTHON() / =PY() Calc add-in + warm venv worker |
=PROMPT(), chat sidebar, MCP, grammar |
| Run Python Script…, document scripts, init script, Reset Python Session | Chat tools (run_venv_python_script, analyze_data, extract_text_from_image, …) |
| Monaco (Edit Python in Cell…, Run Python Script editor) | Analysis Sub-Agent, tool loop, LLM client |
| LibrePy Python sidebar (Calc deck: cells + diagnostics; also in WriterAgent.oxt) | WriterAgent chat deck (WriterAgentDeck) |
| NumPy domain trusted helpers (Analysis, Viz, Symbolic, Units, Forecast, Optimize, Quant, Text Analytics menu) | Embeddings (plugin/embeddings/, folder FTS, hybrid search) |
| Vision/OCR (Run Python Script Vision Helpers + settings) | DuckDB SQL (domain=sql, spreadsheet SQL helpers) |
| TeX/Math (Insert LaTeX Math + Writer HTML math in Run Python Script egress) | Jupyter notebook import (import_ipynb) |
| Settings → Python + venv self-check | Calc spreadsheet → Python import (convert_spreadsheet_to_python) |
Confirmed: Core ships menu + formula surfaces only. Chat tool wrappers stay in WriterAgent even when they call the same trusted compute modules.
Explicit exclusions (do not register menus, trusted domains, or probe groups for these in a core OXT):
- Embeddings —
plugin/embeddings/,WORKER_POOL_EMBEDDINGS,embeddings_*/embedding/langdetecttrusted domains - DuckDB —
plugin/scripting/venv/duckdb_sql.py,SCRIPT_ORIGIN_SQL,domain=sql - Jupyter —
plugin/notebook/,scripting.import_ipynb - Spreadsheet import —
plugin/calc/spreadsheet_import/(proposed),calc.convert_spreadsheet_to_python - Calc-parity
xlhelpers — not in LibrePy today; see Calc-parityxlhelpers (deferred) below.
WriterAgent ships plugin/scripting/calc_functions.py and plugin/scripting/venv/calc_functions_*.py — 259 Calc/Excel formula parity helpers auto-imported as calc in the venv (e.g. calc.sumif(...), calc.xlookup(...)). They exist so spreadsheet import can emit compact Python instead of pasting inline def blocks per workbook (calc-spreadsheet-to-python-import.md). This is not the same as Microsoft Python in Excel’s xl() range bridge (comparison).
LibrePy (core OXT) excludes them for now (~134 KB source; filtered in scripts/librepy_bundle_paths.py via LIBREPY_CALC_FUNCTIONS_EXCLUDES). Rationale:
- Minimal core first — Layers 0–6 should prove
=PY(), Run Python Script, domain helpers, Monaco, Vision, and TeX before adding spreadsheet-conversion surface area. - Spreadsheet import is WriterAgent-only — menu, translator, and chat tool are not in the core bundle; nothing in LibrePy menus calls
calc.*today. - Coverage not fully exercised in core QA — parity tests live in WriterAgent (
tests/scripting/test_calc_functions.py); bundling without a core conversion workflow would ship dead weight for most installs.
What LibrePy still ships: calc_functions_common.py — host-side name frozensets for Analysis, Viz, Forecast, etc. (no NumPy on the LO host).
Runtime without the library: inject_auto_imports skips calc when the module is absent; =PY() and Run Python Script work with np/pd and domain helpers. Only calc.* in user scripts or converted formulas would fail.
Likely re-include later when spreadsheet conversion moves into core and parity is validated — remove LIBREPY_CALC_FUNCTIONS_EXCLUDES from the bundle filter; no refactor required. Power users may also want calc.* in =PY() cells even before the conversion menu ships; that is a reasonable follow-on once tests and docs catch up.
| Concern | Core extension | WriterAgent extension |
|---|---|---|
| Change rate | Low: Calc add-in, subprocess protocol, trusted helpers, Monaco | High: models, tools, UI, MCP |
| Stability | Suitable to ship with LibreOffice core | Third-party / frequent releases |
| Scope | =PY(), scientific menus, Monaco, OCR, TeX |
Chat, =PROMPT(), embeddings, duckdb, jupyter, grammar |
WriterAgent registers =PYTHON() / =PY() and =PROMPT() as separate UNO components (python_addin.py, prompt_addin.py). A core split should ship only the Python add-in and not register a second competing PYTHON implementation.
The full core OXT is the union of all layers below. Each layer adds whole files; later layers depend on earlier ones.
flowchart TB
L0["Layer 0: =PYTHON core"]
L1["Layer 1: Trusted RPC + venv compute"]
L2["Layer 2: Run Python Script + egress"]
L3["Layer 3: NumPy domain helpers"]
L4["Layer 4: Monaco editor"]
L5["Layer 5: Vision/OCR"]
L6["Layer 6: TeX/Math import"]
L0 --> L1 --> L2 --> L3
L2 --> L4
L3 --> L5
L2 --> L6
| Layer | Enables | Detail section |
|---|---|---|
| 0 | =PYTHON() / =PY() formula |
Layer 0 |
| 1 | Trusted helper RPC (required for layers 3–5) | Layer 1 |
| 2 | Run Python Script…, Reset Session, document scripts | Layer 2 |
| 3 | Analysis, Viz, Symbolic, Units, Forecast, Optimize, Quant, Text Analytics | Appendix E |
| 4 | Edit Python in Cell…, Monaco Run/Save | Appendix F |
| 5 | Vision Helpers, Vision OCR Settings | Appendix G |
| 6 | Insert LaTeX Math…, HTML math in Writer insert | Appendix D |
LibreOffice’s embedded Python must not import NumPy/pandas from arbitrary user installs (ABI mismatch → crash). User Python runs in a separate venv interpreter over length-prefixed Pickle5 frames. Trusted helpers use the same warm child via action: "run_trusted_action" (no AST sandbox inside reviewed modules).
flowchart TB
subgraph loHost [LibreOffice host process]
PY["=PY(code, data?)"]
RPS[Run Python Script menu]
PF[python/function.py]
PR[python_runner.py]
TR[trusted_rpc.py]
VW[venv_worker.py]
CFG[framework.config]
end
subgraph child [User venv subprocess]
WH[venv/worker_harness.py]
TD[venv/trusted_dispatch.py]
VS[venv/venv_sandbox.py]
LPE[local_python_executor]
DOM[venv/analysis viz symbolic ...]
end
PY --> PF --> VW
RPS --> PR --> VW
PR --> TR --> VW
VW --> CFG
VW -.->|stdin/stdout Pickle5| WH
WH --> VS --> LPE
WH --> TD --> DOM
Recalc constraint: =PYTHON() runs synchronously during Calc recalc. The implementation deliberately avoids UI event pumping on this path (see comments in function.py) so the formula engine is not re-entered.
Config keys (from plugin/scripting/module.yaml):
| Key | Role |
|---|---|
scripting.python_venv_path |
User venv directory; empty → sys.executable (LO embedded Python, stdlib-only unless extras installed there) |
scripting.python_session_mode |
isolated (default) or shared (workbook namespace for =PY() cells) |
scripting.python_exec_timeout |
Wall-clock seconds per user script run (default 10, clamp 1–600) |
scripting.python_auto_spill |
Auto-spill list/DataFrame returns from single-cell =PY() |
Stored in writeragent.json today (plugin/framework/config.py). A core extension would choose whether to reuse that file, use a dedicated JSON name, or bind LO Tools → Options.
IPC detail: numpy-serialization.md.
Python loads whole modules. If a file is imported, the entire file ships in the OXT, even if only one function is called.
Call chain:
PythonFunction.python() → execute_python_addin → calc_addin_data / payload_codec → run_code_in_user_venv → PythonWorkerManager → venv/worker_harness → venv/venv_sandbox → LocalPythonExecutor
Error display: format_error_for_display → framework.errors / framework.client.errors / i18n.
Config: get_config_str("scripting.python_venv_path"), session mode, timeout, auto-spill.
This closure does not call the LLM, chat panel, or MCP.
=PY(): registerpython_addin.pyonly → loadsfunction.py(noLlmClient).=PROMPT(): registerprompt_addin.py→ loadsprompt_function.py(LLM stack).
A core OXT must not register prompt_addin.py / prompt_function.py. See Recommended refactors.
| File | Why it ships |
|---|---|
plugin/framework/config.py |
Config read/write; WriterAgentConfig + MODULES |
plugin/framework/constants.py |
get_plugin_dir, AUTO_IMPORTS, worker pool ids |
plugin/framework/errors.py |
format_error_payload, ConfigError, safe_call |
plugin/framework/json_utils.py |
safe_json_loads (via client/errors.py) |
plugin/framework/i18n.py |
_() for translated errors |
plugin/framework/event_bus.py |
global_event_bus — imported by config.py |
plugin/framework/service.py |
ServiceBase — imported by event_bus.py |
plugin/framework/url_utils.py |
Endpoint normalization — imported by config.py |
plugin/framework/thread_guard.py |
background — imported by venv_worker.py |
plugin/framework/client/errors.py |
format_error_for_display in python() handler |
plugin/framework/client/__init__.py |
Package |
plugin/framework/__init__.py |
Package |
plugin/_manifest.py |
Generated (make manifest). config_limits.py reads MODULES for schema defaults |
Not required for Layer 0 unless a higher layer pulls them in: llm_client.py, async_stream.py, tool.py, default_models.py, uno_context.py, worker_pool.py, appearance.py, …
| File | Role |
|---|---|
plugin/calc/python/addin.py |
UNO add-in: python() |
plugin/calc/python/function.py |
execute_python_addin, matrix session, spill, finalize_python_return |
plugin/calc/addin_common.py |
Shared add-in helpers |
plugin/calc/calc_addin_data.py |
Range → data, size limits, wire packing; _resolve_python_data for Run Python Script / trusted helpers |
plugin/calc/__init__.py |
Package (CalcError) |
plugin/calc/prompt_addin.py |
WriterAgent only — do not register in core |
plugin/calc/prompt_function.py |
WriterAgent only |
| File | Role |
|---|---|
plugin/scripting/venv_worker.py |
run_code_in_user_venv, PythonWorkerManager, warm worker, venv resolution |
plugin/scripting/ipc.py |
Pickle5 frame read/write |
plugin/scripting/payload_codec.py |
split_grid wire codec (host + child) |
plugin/scripting/sandbox.py |
VENV_AUTHORIZED_IMPORTS, scrub_subprocess_env, resolve_venv_python |
plugin/scripting/config_limits.py |
Timeout defaults/min/max, warm/long-trusted budgets |
plugin/scripting/session_manager.py |
Shared-kernel workbook sessions, reset |
plugin/scripting/module.yaml |
Config schema for Python settings |
plugin/scripting/__init__.py |
Package |
| File | Role |
|---|---|
plugin/scripting/venv/worker_harness.py |
Child stdin/stdout loop, trusted-action handler |
plugin/scripting/venv/venv_sandbox.py |
LocalPythonExecutor, user-code sandbox |
plugin/scripting/venv/coerce.py |
Grid → DataFrame coercion |
plugin/scripting/venv/__init__.py |
Package |
| File | Role |
|---|---|
plugin/contrib/smolagents/local_python_executor.py |
Restricted executor |
plugin/contrib/smolagents/tools.py |
Tool type required by executor |
plugin/contrib/smolagents/utils.py |
BASE_BUILTIN_MODULES, helpers |
plugin/contrib/smolagents/agent_types.py |
Imported by tools.py |
plugin/contrib/smolagents/tool_validation.py |
Imported by tools.py |
plugin/contrib/smolagents/_function_type_hints_utils.py |
Imported by tools.py |
plugin/contrib/smolagents/__init__.py |
Package |
LibrePy bundle: scripts/build_librepy_oxt.py replaces smolagents/__init__.py with a slim stub (no agents import) so the venv worker can load local_python_executor without shipping chat-only smolagents modules.
| plugin/contrib/__init__.py | Package |
| File | Role |
|---|---|
plugin/__init__.py |
Package — must use pkgutil.extend_path when core and WriterAgent ship different plugin/ subtrees in separate OXTs (Prototype extension §2) |
Required for NumPy domain helpers, Vision OCR, and any run_trusted_worker_action path. Builds on Layer 0.
| File | Role |
|---|---|
plugin/scripting/client.py |
run_* host stubs, long-trusted timeout list |
plugin/scripting/trusted_rpc.py |
run_trusted_worker_action |
plugin/scripting/trusted_action_registry.py |
Domain → venv dispatcher wiring |
plugin/scripting/venv/trusted_dispatch.py |
Routes run_trusted_action to domain run_* |
plugin/scripting/venv/worker_heartbeat.py |
Heartbeat frames (long jobs) |
plugin/scripting/_lazy_venv.py |
Lazy plugin.scripting.* → plugin.scripting.venv.* |
plugin/scripting/helper_domain.py |
Shared helper-domain glue |
plugin/scripting/domain_registry.py |
RPS fast-path, picker origins, post-venv routing |
plugin/scripting/calc_functions_common.py |
Helper name frozensets (no numpy on host) |
plugin/scripting/import_policy.py |
LLM import-policy text generation |
plugin/scripting/venv_diagnostics.py |
Settings → Python Test self-check |
Core-build note: Filter trusted_action_registry.py to numpy + vision domains only (units, symbolic, math, viz, analysis, forecast, optimize, quant, text, vision). Omit embeddings_*, sql, embedding, langdetect, languagetool, vale, harper. The whole trusted_dispatch.py file still ships unless maintainers split it; excluded domains become dead code.
Trusted RPC flow:
flowchart LR
host[Host facade e.g. viz.py]
client[client.py run_trusted_*]
rpc[trusted_rpc.py]
vw[venv_worker.py]
wh[worker_harness.py]
td[trusted_dispatch.py]
compute[venv/analysis.py etc]
host --> client --> rpc --> vw --> wh --> td --> compute
WriterAgent exposes Run Python Script… (extension/Addons.xcu → scripting.run_python_dialog), Reset Python Session (scripting.reset_python_session), and Text Analytics… (textanalytics.open_dialog). Wired from plugin/main.py or an equivalent core bootstrap job.
Entry: run_python_dialog(). Reuses run_code_in_user_venv. After success, writes into the active document (Writer or Calc). Draw/Impress shows an info message only.
flowchart LR
menu[Run Python Script menu]
dlg[Monaco or native dialog]
venv[run_code_in_user_venv]
w[Writer: format_result_for_writer]
insW[insert_content_at_position HTML]
c[Calc: insert_result_into_calc]
insC[write_formula_range from selection]
menu --> dlg --> venv
venv --> w --> insW
venv --> c --> insC
| App | Insertion | Result shaping |
|---|---|---|
| Writer | HTML at selection via insert_content_at_position |
format_result_for_writer — lists → tables, dicts → sections |
| Calc | Values from active selection via insert_result_into_calc |
Dict title/summary + tables; 1D/2D lists via write_formula_range |
| Draw/Impress | None (message box) | — |
Config keys: last_python_script_writer, last_python_script_calc, last_python_script_draw.
| File | Role |
|---|---|
plugin/scripting/python_runner.py |
Dialog, run, domain fast-paths, Writer/Calc branch |
plugin/scripting/python_runner_ui.py |
Native XDL fallback when Monaco unavailable |
plugin/scripting/document_scripts.py |
Document-attached scripts, init script storage |
plugin/chatbot/dialogs.py |
add_dialog_*, msgbox |
plugin/framework/uno_context.py |
get_ctx, get_desktop |
plugin/framework/worker_pool.py |
Via dialogs.py |
plugin/framework/appearance.py |
LO light/dark → Monaco theme |
plugin/doc/document_helpers.py |
is_writer / is_calc / is_draw — pulls calc.bridge + calc.analyzer at import |
plugin/doc/visual_helpers.py |
Graphic export for Vision egress |
plugin/writer/format.py |
HTML insert, mixed math segments |
plugin/writer/ops.py |
Selection/cursor helpers |
plugin/writer/review_authors.py |
Via format.py |
plugin/writer/xhtml_style_postprocess.py |
HTML post-process |
plugin/calc/bridge.py |
Active sheet / document access |
plugin/calc/address_utils.py |
index_to_column for anchor cell |
plugin/calc/manipulator.py |
write_formula_range |
plugin/calc/tabular_egress.py |
Tabular helper results → sheet |
plugin/calc/rich_html.py |
Rich HTML cell insert (Vision Calc egress) |
plugin/main.py |
Action handlers — or plugin/main_core.py for core-only bootstrap (see Prototype extension) |
Refactor note: plugin/doc/doc_type.py with is_writer/is_calc only would avoid loading calc.analyzer for Writer menus.
Optional gettext: filtered catalogs via make compile-translations-core (part of make build-core) — scripts/build_librepy_locales.py extracts strings from the LibrePy file closure only and bundles slim .mo files from build/generated/locales/, not the full WriterAgent locales/ tree.
| Artifact | Notes |
|---|---|
extension/idl/XPythonFunction.idl |
python(in string code, in any data) |
extension/idl/XPromptFunction.idl |
WriterAgent only — prompt() |
extension/XPythonFunction.rdb, XPromptFunction.rdb |
Built from IDL — scripts/rebuild_xprompt_rdb.sh |
extension/registry/.../CalcAddIns.xcu |
Core: python / PY node only; no prompt |
extension/META-INF/manifest.xml |
Filtered UNO entries + Python tree |
description.xml |
New extension identifier if not WriterAgent |
Service: com.sun.star.sheet.AddIn
Core menus (extension/Addons.xcu)
| Action | Core | WriterAgent only |
|---|---|---|
scripting.run_python_dialog |
Yes | |
scripting.edit_python_cell |
Yes (Layer 4) | |
scripting.reset_python_session |
Yes | |
writer.insert_latex_dialog |
Yes (Layer 6) | |
vision.open_settings |
Yes (Layer 5) | |
textanalytics.open_dialog |
Yes (Layer 3) | |
scripting.import_ipynb |
Yes | |
calc.convert_spreadsheet_to_python |
Yes | |
embeddings.search_dialog |
Yes | |
| Chat / review accelerators | Yes |
From scripting/module.yaml and vision/module.yaml:
- Settings → Python tab (
SettingsDialog.xdlpages) PythonTestProgressDialog.xdl(venv Test)PythonScriptDialog.xdl(native Run Python fallback)LatexInputDialog.xdl(native LaTeX fallback)VisionSettingsDialog.xdlMsgBoxWithCopyDialog.xdl,ErrorReportDialog.xdl(as used by dialogs)
Manifest reference: scripts/manifest_registry.py.
| Path | Layer | Notes |
|---|---|---|
vendor/latex2mathml/ |
6 | make vendor from requirements-vendor.txt; on sys.path at bootstrap |
vendor/json_repair/ |
0–2 | Config read + json_utils.py robust JSON parse |
User venv rocher |
4 | Monaco UI assets — not in OXT |
| User venv scientific stack | 0–5 | numpy, docling, pywebview, etc. — user-maintained |
LibrePy vendor subset: build_librepy_oxt.py copies only json_repair and latex2mathml from vendor/ into plugin/lib/ (LIBREPY_VENDOR_PACKAGES). WriterAgent-only vendored packages are omitted from the core OXT:
| Package | WriterAgent use | LibrePy |
|---|---|---|
snowballstemmer |
Grammar stemming, web-research fluff words | Excluded |
websockets |
CDP browser tools (plugin/contrib/cdp/) |
Excluded |
defusedxml |
Embeddings locale XML (plugin/embeddings/) |
Excluded |
Full make vendor still installs all entries in requirements-vendor.txt for WriterAgent builds.
| Step | Detail |
|---|---|
| Manifest | make manifest → plugin/_manifest.py from module.yaml files. Core: at least scripting + vision; no embeddings |
| Bundle | Same OXT pipeline as WriterAgent, filtered to layer file lists; vendor copy uses LIBREPY_VENDOR_PACKAGES (json_repair, latex2mathml only) |
| Config path | Linux: ~/.config/libreoffice/{4,24}/user/writeragent.json (see AGENTS.md Config) |
These share the venv worker or trusted RPC with core but must not ship in a core OXT (or require the full WriterAgent chat/LLM stack).
| Surface | Entry | Uses venv? | Inserts? |
|---|---|---|---|
Chat run_venv_python_script |
plugin/calc/python/venv.py |
Yes | No — JSON to agent |
Chat execute_python_script |
plugin/calc/python/executor.py |
No (embedded LO Python) | Optional target_range |
Additional files: plugin/calc/base.py, plugin/calc/inspector.py, plugin/framework/tool.py, full plugin/chatbot/* tool loop.
Tests: tests/calc/python/test_venv.py, tests/calc/python/test_executor.py
| Tool | Module | Core equivalent |
|---|---|---|
analyze_data |
plugin/calc/analysis.py |
Run Python Script → Analysis Helpers |
plot_data |
plugin/calc/viz.py |
Run Python Script → Viz Helpers |
forecast_data |
plugin/calc/forecast.py |
Run Python Script → Forecast |
optimize_data |
plugin/calc/optimize.py |
Run Python Script → Optimize |
symbolic_math |
plugin/calc/symbolic_math.py |
Run Python Script → Math Helpers |
extract_text_from_image |
plugin/vision/vision_tools.py |
Run Python Script → Vision Helpers |
run_venv_python_script on Writer ignores data_range; the model inserts via Writer HTML tools, not python_runner. Menu-driven Writer insert: Layer 2.
| Area | Examples |
|---|---|
| LLM / chat | plugin/framework/client/llm_client.py, plugin/chatbot/*, =PROMPT() |
| Embeddings | plugin/embeddings/, WORKER_POOL_EMBEDDINGS |
| DuckDB | plugin/scripting/duckdb_sql.py, SQL picker origin |
| Jupyter | plugin/notebook/, import_ipynb |
| Spreadsheet import | calc.convert_spreadsheet_to_python |
| Grammar | languagetool, vale, harper venv modules |
| MCP / grammar UI | plugin/mcp/, plugin/writer/locale/ |
| Analysis Sub-Agent | analysis-sub-agent.md |
| Sidebar audio mic | plugin/chatbot/audio_recorder.py — Settings may probe sounddevice; capture UI is chat |
flowchart TB
subgraph corePaths [Core extension]
PY["=PY()"]
menu[Run Python Script]
helpers[Trusted domain helpers]
end
subgraph writerAgent [WriterAgent only]
chat[Chat tool loop]
toolV[run_venv_python_script]
toolA[analyze_data etc]
end
PY --> helpers
menu --> helpers
chat --> toolV --> helpers
chat --> toolA --> helpers
- Split the add-in —
python_addin.pywith zero imports fromllm_clientorasync_stream. - Slim configuration —
python_config.pywith Python keys only instead of fullWriterAgentConfig. - Narrow IDL —
XPythonFunction.idlonly in core RDB. - Separate extension id — avoid
org.extension.writeragent.*if WriterAgent remains distinct. - WriterAgent integration — remove duplicate
=PY()registration; declare dependency on core OXT (Prototype extension). - Slim
trusted_action_registry.py— core flavor: numpy + vision domains only. - Slim
venv_diagnostics.py— drop Embeddings Libraries probe; optional Audio probe (sidebar is WriterAgent). - Slim
domain_registry.py— removeSCRIPT_ORIGIN_SQLand embeddings origins. doc_type.py— replace heavydocument_helpers.pyimports for menu guards.- Split
writer/images/images.py— Vision graphic export vs LLM image generation. - Filtered
Addons.xcu/main.py— core usesmain_core.py; WriterAgent drops python menu handlers. pkgutil.extend_pathinplugin/__init__.py— required for dual-OXTplugin/split.- Optional sandbox slimming — minimal
Toolstub instead of full smolagentstools.pychain.
| Option | Summary | Status |
|---|---|---|
| A — Core only (LibrePy.oxt) | Core OXT alone: =PY(), scientific menus, no chat. |
Supported today — make deploy-core |
| B — Core + WriterAgent | Core owns =PY() and the scripting stack; WriterAgent is AI-only overlay (=PROMPT(), chat, MCP). |
Not shipped — target architecture below |
| C — Duplicate (avoid) | Both extensions register =PY() / PYTHON or both ship full plugin/scripting/ → add-in conflict and/or import shadowing |
Avoid |
| D — WriterAgent only | Full WriterAgent OXT with its own Python stack (no LibrePy). | Supported today — make deploy; do not leave LibrePy installed at the same time |
Today: install LibrePy xor WriterAgent, not both. register-librepy-oxt removes WriterAgent first. WriterAgent’s register-built-oxt currently only removes its own id — uninstall LibrePy manually before switching back until deploy symmetry is finished.
Target (not yet): B — core is standalone; WriterAgent assumes LibrePy is installed and does not register =PY() or duplicate scientific menus.
Prototype extension (standalone core; WriterAgent overlay later) {#prototype-extension-standalone-core--writeragent-overlay}
How this repo ships LibrePy.oxt so it works by itself (including =PY() / =PYTHON()), and the remaining steps for WriterAgent to become an optional AI layer that imports the core Python stack instead of duplicating it.
Shipped: standalone LibrePy (option A).
Not shipped: dual-install overlay (option B) — see Shipped so far.
flowchart TB
subgraph coreOXT [Core OXT standalone]
PY["=PY / =PYTHON"]
menus[Run Python Script Monaco Vision TeX]
worker[venv_worker trusted helpers]
cfg[writeragent.json scripting keys]
end
subgraph waOXT [WriterAgent OXT optional]
chat[Sidebar chat tools]
prompt["=PROMPT()"]
mcp[MCP grammar embeddings]
end
coreOXT --> PY
waOXT -->|import plugin.scripting.*| coreOXT
waOXT -->|no python add-in| coreOXT
| Install set | Expected | Status |
|---|---|---|
| Core only (LibrePy) | =PY() works; Run Python Script, Monaco, domains, Vision, LaTeX; Settings → Python; Python sidebar (Calc); no chat sidebar |
Shipped |
| Core + WriterAgent | Same single =PY() add-in; chat tools call plugin.scripting.venv_worker from core; =PROMPT() from WriterAgent only |
Not shipped |
| WriterAgent only | Full WriterAgent Python stack + AI + Python sidebar (superset of LibrePy surfaces) | Shipped (exclusive of LibrePy) |
| WriterAgent only after strip (no LibrePy, no bundled scripting) | Unsupported — needs LibrePy for =PY() |
Future under option B |
Do not reuse org.extension.writeragent for the core OXT — unopkg must be able to install both side by side. Example prototype id: org.extension.librepy (rename for upstream as needed).
| Artifact | WriterAgent today | Core prototype |
|---|---|---|
| Extension id | org.extension.writeragent |
org.extension.librepy |
description.xml |
extension/description.xml.tpl |
extension-core/description.xml (new identifier + display name) |
| UNO add-in impl | org.extension.writeragent.PythonFunction |
org.extension.writeragent.PythonFunction (alias — same namespace as WriterAgent so ORG.EXTENSION.WRITERAGENT.PYTHONFUNCTION.* formulas are portable; extension id stays org.extension.librepy) |
| IDL / RDB | org.extension.writeragent.PythonFunction.XPythonFunction |
Same IDL module path as WriterAgent (extension-core/idl/XPythonFunction.idl); rebuild via make rdb-core |
| CalcAddIns node | org.extension.writeragent.PythonFunction |
org.extension.writeragent.PythonFunction — keep function names py and python (users still type =PY()) |
| Protocol handler | org.extension.writeragent:* |
org.extension.librepy:* |
| Menu URLs | org.extension.writeragent:scripting.run_python_dialog |
org.extension.librepy:scripting.run_python_dialog |
| Startup job | org.extension.writeragent.Main |
org.extension.librepy.Main |
| Menubar node | org.extension.writeragent.menubar |
org.extension.librepy.menubar |
Update hardcoded strings in addin_librepy.py (implementationName, IDL import) — registers as org.extension.writeragent.PythonFunction while menus/protocol use org.extension.librepy. scripts/manifest_registry.py uses _PROTOCOL = "org.extension.writeragent" — core build needs a parallel constant or template substitution.
Core registers =PY() / =PYTHON() only. Do not register prompt_addin / =PROMPT() in core.
Both extensions use the top-level package name plugin. LibreOffice prepends each extension root to sys.path. Without a namespace package, whichever extension wins on sys.path owns all of plugin.* — the other extension’s subpackages may be invisible.
Required in both OXTs — update plugin/__init__.py:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)Then:
| OXT | Ships under plugin/ (examples) |
|---|---|
| Core | scripting/, calc/python/{addin,function,editor,...}, framework/ (config subset), writer/format, vision/, main_core.py, … — Layers 0–6 |
| WriterAgent | chatbot/, mcp/, calc/prompt_*, calc/base.py, calc/python/venv.py (chat tool), calc/analysis.py (chat tools), framework/prompts.py, embeddings/, main.py, framework/client/llm_*, … — WriterAgent-only |
Chat run_venv_python_script imports plugin.scripting.venv_worker — resolved from core’s extension root. WriterAgent must not bundle duplicate plugin/scripting/ in its OXT after the strip.
Remove from WriterAgent when core is the Python owner:
Manifest / UNO
plugin/calc/python/addin.pyextension/XPythonFunction.rdb(keepXPromptFunction.rdbfor=PROMPT())
Registry
- Entire
org.extension.writeragent.PythonFunctionnode inCalcAddIns.xcu— leave onlyPromptFunction/prompt.
Menus — remove from WriterAgent Addons.xcu (core owns these):
scripting.run_python_dialogscripting.edit_python_cellscripting.reset_python_sessionwriter.insert_latex_dialogvision.open_settingstextanalytics.open_dialog
Bootstrap — plugin/main.py: drop handlers for the above; keep chat, LLM settings, MCP, grammar, embeddings, review toolbar, etc.
OXT bundle — do not package core layer files (Summary inventory) in WriterAgent; only ship AI-specific trees.
Extension dependency — add to WriterAgent description.xml.tpl:
<dependencies>
<l:LibreOffice-minimal-version d:name="LibreOffice 24.8" value="24.8"/>
<OpenOffice.org-extension name="org.extension.librepy" optional="false"/>
</dependencies>Optional runtime guard: if core is missing, log a clear error and disable chat Python tools (not required if you always assume core is installed).
Core should not load chat, MCP, or grammar. Add plugin/main_core.py (or filter main.py in the core build) that:
- Puts
vendor/onsys.path(forlatex2mathml) - Calls
init_config(ctx) - Registers only core actions:
run_python_dialog,edit_python_cell,reset_python_session,insert_latex_dialog,vision.open_settings,textanalytics.open_dialog, Settings → Python
Register as org.extension.librepy.Main in a core-only Jobs.xcu and META-INF/manifest.xml.
Recommended: keep writeragent.json for both extensions (CONFIG_FILENAME) so venv path, session mode, and timeouts are shared. Core owns Python Settings (plugin/librepy/settings.py: Python tab only; General/Image tabs hidden); WriterAgent Settings focus on LLM / AI keys.
Shared Settings helpers (both OXTs): plugin/scripting/venv_probe_ui.py (venv Test / download progress modal) and plugin/chatbot/settings_fields.py (module.yaml field-spec build/apply). LibrePy keeps Python-only chrome and Cython-only download local; list new shared files in scripts/librepy_bundle_paths.py.
WriterAgent-only module.yaml settings keys can carry librepy_exclude: true (e.g. scripting.ppt_master_data_path). LibrePy manifest generation (make manifest-core / generate_manifest.py --skip-writeragent-extension) omits those keys from _manifest_librepy.py and from generated SettingsDialog.xdl page 3 controls.
One venv + one shared-kernel session for =PY() and chat run_venv_python_script — desirable when both are installed.
Alternative for isolation: librepy.json + slim python_config.py in core only (more work; only if you need separate config files).
Suggested layout without forking all of plugin/:
extension-core/ # parallel to extension/
description.xml # org.extension.librepy
META-INF/manifest.xml # layers 0–6 UNO entries only
Jobs.xcu # org.extension.librepy.Main
ProtocolHandler.xcu # org.extension.librepy:*
Addons.xcu # core menus only
registry/CalcAddIns.xcu # PythonFunction only (no PROMPT)
idl/XPythonFunction.idl # librepy namespace
plugin/
main_core.py # slim bootstrap (new)
__init__.py # pkgutil.extend_path (both OXTs)
Makefile (suggested targets)
make build-core / deploy-core # LibrePy.oxt — **shipped** (removes WriterAgent on register)
make build / deploy # WriterAgent OXT (keep exclusive of LibrePy until overlay lands)
LibrePy menu Context: In extension-core/Addons.xcu, every submenu item must set an explicit Context property. Do not rely on “empty Context = all applications” when the same submenu mixes Writer-only and Calc-only entries — LibreOffice may hide shared items (Settings, Run Python Script, Reset Python Session) in Calc. Shared items use the full menubar context string (Writer, Calc, Draw, Impress, Web, Global); doc-specific items set TextDocument or SpreadsheetDocument only. Regression test: tests/scripts/test_librepy_addons_xcu.py.
make manifestfor core: includescripting+visionmodule.yamlonly; excludeembeddings.make bundle-core: copy/filter files from Layers 0–6; substituteorg.extension.librepyin XCU/IDL/addin.make deploy-core:unopkg remove/add org.extension.librepy(parallel to existingorg.extension.writeragentdeploy).
AddDonepkgutil.extend_pathtoplugin/__init__.py.AddDoneextension-core/skeleton +org.extension.librepyidentifiers +main_core.py.Done (LibrePy.oxt)build-core/deploy-core; verify core alone (=PY(), menus, Settings → Python Test).- Slim WriterAgent manifest (no
python_addin, no python menus, no duplicatescripting/in OXT). Not done - Add extension dependency in WriterAgent
description.xml. Not done - Verify core + WriterAgent: one
=PY()add-in, chat Python tools work via core worker. Not done — deploy still mutually excludes the other OXT
- Do not install both OXTs with both registering
py/pythonin CalcAddIns — Calc shows duplicate add-ins; behavior is undefined. LibrePy already registersorg.extension.writeragent.PythonFunction; WriterAgent must skip its own PythonFunction when LibrePy is present. - Do not ship two full copies of
plugin/scripting/withoutextend_path— import shadowing is nondeterministic. - Do not use
org.extension.writeragentas the core extension id — conflicts with existing WriterAgentunopkgidentity and protocol namespace.
Manual QA fixture: tests/fixtures/numpy_domains_demo.ods — all domain helpers on one workbook (numpy_domains_demo.README.md).
| Component | License |
|---|---|
| WriterAgent (KeithCu / John Balis modifications) | GPL-3.0+ |
Vendored plugin/contrib/smolagents/ (Hugging Face) |
Apache-2.0 — retain notices in core OXT |
Vendored vendor/latex2mathml/ |
Per upstream license in vendor tree |
| In-process Calc sandbox lineage | Apache-2.0 note in executor.py (WriterAgent only; core uses venv path) |
Deduplicated union of Layers 0–6. Counts are approximate (~100 plugin/ paths); verify with import closure on a filtered bundle.
Framework (13): config.py, constants.py, errors.py, json_utils.py, i18n.py, event_bus.py, service.py, url_utils.py, thread_guard.py, client/errors.py, client/__init__.py, framework/__init__.py, _manifest.py
Calc (5): python/addin.py, python/function.py, addin_common.py, calc_addin_data.py, calc/__init__.py
Scripting host (8): venv_worker.py, ipc.py, payload_codec.py, sandbox.py, config_limits.py, session_manager.py, module.yaml, scripting/__init__.py
Venv child (4): venv/worker_harness.py, venv/venv_sandbox.py, venv/coerce.py, venv/__init__.py
Contrib smolagents (8): local_python_executor.py, tools.py, utils.py, agent_types.py, tool_validation.py, _function_type_hints_utils.py, smolagents/__init__.py, contrib/__init__.py
Root: plugin/__init__.py
client.py, trusted_rpc.py, trusted_action_registry.py, venv/trusted_dispatch.py, venv/worker_heartbeat.py, _lazy_venv.py, helper_domain.py, domain_registry.py, calc_functions_common.py, import_policy.py, venv_diagnostics.py
python_runner.py, python_runner_ui.py, document_scripts.py, chatbot/dialogs.py, uno_context.py, worker_pool.py, appearance.py, doc/document_helpers.py, doc/visual_helpers.py, writer/format.py, writer/ops.py, writer/review_authors.py, writer/xhtml_style_postprocess.py, calc/bridge.py, calc/address_utils.py, calc/manipulator.py, calc/tabular_egress.py, calc/rich_html.py, main.py
Host facades: analysis.py, viz.py, symbolic.py, units.py, forecast.py, optimize.py, quant.py, text_analytics.py, text_analytics_ui.py
Venv compute: venv/analysis.py, venv/viz.py, venv/symbolic.py, venv/units.py, venv/forecast.py, venv/optimize.py, venv/quant.py, venv/text_analytics.py
Calc runners/egress: calc/analysis_runner.py, calc/analysis_egress.py, calc/viz_auto_plot.py, calc/forecast_auto_plot.py, calc/quant_egress.py, calc/python/image_egress.py, calc/inspector.py
Writer images: writer/images/image_tools.py, writer/images/image_utils.py, writer/images/__init__.py — note images.py pulls LLM image gen if imported
editor_host.py, editor_ipc.py, calc/python/editor.py, calc/python/formula_edit.py, calc/python/editor_context_menu.py, calc/python/workbook_lifecycle.py, venv/editor_main.py, calc/excel_py_convert/ (Excel Python-in-Excel → DAG =PY auto-convert on open)
Dev reference only (not OXT): contrib/scripting/assets/editor/*
vision/__init__.py, vision/module.yaml, vision_common.py, vision_templates.py, vision_runner.py, vision_egress.py, vision_availability.py, vision/venv/vision.py, vision/venv/vision_docling.py, vision/venv/vision_paddle.py, vision/venv/vision_html_export.py, vision/venv/vision_layout_html.py, vision/venv/__init__.py, calc/vision_egress.py, chatbot/module_config_dialog.py
Omit vision_tools.py for menu-only core (chat extract_text_from_image).
writer/math/latex_dialog.py, writer/math/math_mml_convert.py, writer/math/html_math_segment.py, writer/math/__init__.py
idl/XPythonFunction.idl, XPythonFunction.rdb, registry/.../CalcAddIns.xcu, META-INF/manifest.xml, Addons.xcu, WriterAgentDialogs/* (see Extension packaging), description.xml, vendor/latex2mathml/**
| Group | Packages |
|---|---|
| Scientific / EDA | numpy, pandas, scipy, scikit-learn, statsmodels, ydata-profiling, pandas-montecarlo |
| Viz | matplotlib, seaborn |
| Symbolic | sympy |
| Units | pint |
| Text | spacy, textdescriptives, … |
| Quant | yfinance, pandas_ta, … |
| Vision | docling, rapidocr-paddle, pillow, css-inline |
| Monaco | pywebview, rocher, PyQt6, PyQt6-WebEngine, qtpy |
See numpy-domains.md and image-recognition.md for authoritative lists.
prompt_addin.py, prompt_function.py, plugin/framework/prompts.py, plugin/calc/base.py, plugin/embeddings/**, plugin/notebook/**, plugin/scripting/venv/duckdb_sql.py, plugin/calc/spreadsheet_import/**, plugin/scripting/calc_functions.py, plugin/scripting/venv/calc_functions*.py (deferred — see § Scope), plugin/calc/python/venv.py, plugin/calc/analysis.py (chat tool), plugin/vision/vision_tools.py (if menu-only), plugin/framework/client/llm_client.py, plugin/chatbot/panel.py, grammar venv modules, full chat stack.
- Enabling NumPy & Python in LibreOffice — user guide, architecture,
=PY()behavior - Calc
=PY()data shapes —CalcRange, blanks/NaN, multi-range - NumPy domain helpers — Analysis, Viz, Symbolic, Units, Forecast, Optimize, Quant, Text
- Venv subprocess IPC & serialization — warm worker, protocol, wire formats
- Monaco editor dev plan — IPC, phases 2B–2F
- Image Recognition — Vision/OCR design
- Math / TeX import — LaTeX, MathML, StarMath pipeline
- Calc integration — broader Calc chat/tools (WriterAgent scope)
Out of scope for core (separate PM/dev docs): embeddings.md, duckdb-calc-dev-plan.md, jupyter-notebook-import.md, calc-spreadsheet-to-python-import.md
WriterAgent exposes Insert LaTeX Math… (writer.insert_latex_dialog) and embeds math in Run Python Script Writer HTML via format.py + html_math_segment.py.
Writer-only. Conversion runs in-process in LibreOffice’s embedded Python plus a hidden LibreOffice Math document load — no venv.
flowchart LR
menu[Insert LaTeX Math menu]
dlg[Monaco or XDL dialog]
l2m[latex2mathml in vendor/]
mml[convert_mathml_to_starmath]
ins[insert_writer_math_formula]
menu --> dlg --> l2m --> mml --> ins
| Step | Implementation |
|---|---|
| Dialog | latex_dialog.py — Monaco mode: latex or native XDL |
| Persist UI | last_latex_input, last_latex_display_block in config |
| LaTeX → MathML | Vendored latex2mathml on sys.path |
| MathML → StarMath | math_mml_convert.py — hidden Math doc |
| Insert | insert_writer_math_formula — TextEmbeddedObject with Math CLSID |
| File | Role |
|---|---|
plugin/writer/format.py |
_insert_mixed_html_and_math_at_cursor, insert_content_at_position |
plugin/writer/math/html_math_segment.py |
$…$, $$…$$, \(...\), \[...\], MathML segments |
plugin/writer/math/math_mml_convert.py |
LaTeX/MathML → StarMath (shared with menu) |
| Path | Role |
|---|---|
vendor/latex2mathml/ |
OXT-bundled via make vendor |
| LibreOffice Math | Required at runtime for conversion |
| Path | Why |
|---|---|
plugin/draw/math_insert.py |
Draw/Impress chat tool (WriterAgent) |
| Venv worker / smolagents | Menu path does not use subprocess |
Deeper design: math-tex.md.
Shipped domains per numpy-domains.md: Analysis, Visualization, Symbolic Math, Units, Forecasting, Optimization, Quant, Text Analytics.
| Domain | Trusted RPC domain |
Menu / RPS | Chat tool (WriterAgent) |
|---|---|---|---|
| Analysis | analysis |
Run Python Script → Analysis Helpers | analyze_data |
| Viz | viz |
Run Python Script → Viz Helpers | plot_data |
| Symbolic | symbolic / math |
Run Python Script → Math Helpers | symbolic_math |
| Units | units |
Run Python Script → Units Helpers | — |
| Forecast | forecast |
Run Python Script → Forecast (header fast-path) | forecast_data |
| Optimize | optimize |
Run Python Script → Optimize (header fast-path) | optimize_data |
| Quant | quant |
Run Python Script → Quant Helpers | — |
| Text | text |
Tools → Text Analytics… | — |
| File | Role |
|---|---|
analysis.py |
Templates, run_trusted_analysis orchestration |
viz.py |
Viz templates, Writer/Calc egress |
symbolic.py |
SymPy helpers, math insert egress |
units.py |
Pint unit conversion |
forecast.py |
Time-series helpers |
optimize.py |
scipy.optimize helpers |
quant.py |
Quantitative finance helpers |
text_analytics.py |
spaCy / textdescriptives orchestration |
text_analytics_ui.py |
Text Analytics menu dialog |
| File | Role |
|---|---|
analysis.py |
run_analysis — full numpy/pandas/scipy stack |
viz.py |
run_viz — matplotlib/seaborn |
symbolic.py |
run_symbolic — SymPy |
units.py |
run_units — Pint |
forecast.py |
run_forecast — statsmodels |
optimize.py |
run_optimize |
quant.py |
run_quant |
text_analytics.py |
run_text_analytics |
Requires Layer 1 + domain_registry.py for RPS picker templates and fast-path headers (writeragent:forecast, etc.).
| File | Role |
|---|---|
calc/analysis_runner.py |
Range resolve + trusted analysis invoke |
calc/analysis_egress.py |
Analysis results → sheet |
calc/viz_auto_plot.py |
Auto-chart after viz |
calc/forecast_auto_plot.py |
Auto-chart after forecast |
calc/quant_egress.py |
Quant results → sheet |
calc/python/image_egress.py |
Matplotlib figure → sheet image |
calc/inspector.py |
read_range for data-bound helpers |
| File | Role |
|---|---|
writer/images/image_tools.py |
Insert plot images at cursor |
writer/images/image_utils.py |
Graphic byte export helpers |
writer/math/math_mml_convert.py |
Symbolic math → Writer formula objects |
Whole-file caveat: writer/images/images.py imports LLM image-generation code; prefer image_tools.py only or split before core bundle.
| Path | Why |
|---|---|
plugin/calc/analysis.py, viz.py, forecast.py, optimize.py, symbolic_math.py |
Chat ToolBase wrappers — WriterAgent |
plugin/scripting/venv/duckdb_sql.py |
DuckDB — excluded |
plugin/embeddings/** |
Embeddings — excluded |
Monaco-based code editor (pywebview child in the user venv) for Calc formulas and ad-hoc scripts. Detail: python-monaco-editor-dev-plan.md.
| Feature | Entry |
|---|---|
| Edit Python in Cell… | calc/python/editor.py |
| Run Python Script… Monaco | python_runner.py + editor_host.py |
| Document scripts | document_scripts.py |
| Init script editor | init_script_editor.py + LibrePy sidebar button; Monaco load/save of document INIT script |
| Theme sync | appearance.py |
flowchart LR
host[editor_host.py in LO]
pipe[editor_ipc.py Pickle5]
child[venv/editor_main.py pywebview]
rocher[rocher Monaco assets in venv]
host --> pipe --> child --> rocher
| File | Role |
|---|---|
plugin/scripting/editor_host.py |
Spawn pywebview child, session, theme |
plugin/scripting/editor_ipc.py |
Pipe framing, errors |
plugin/calc/python/editor.py |
Edit Python in Cell… |
plugin/calc/python/formula_edit.py |
Parse/rebuild =PY() formulas |
plugin/calc/python/editor_context_menu.py |
Cell context menu |
plugin/calc/python/workbook_lifecycle.py |
Init script session reset |
plugin/calc/python/cell_discovery.py |
Enumerate =PY() cells for sidebar |
plugin/calc/python/diagnostics.py |
Bounded stdout/error log for sidebar |
plugin/calc/python/init_script_editor.py |
Monaco editor for workbook INIT script |
plugin/librepy/panel_factory.py |
LibrePy Calc sidebar UNO factory |
plugin/librepy/python_sidebar.py |
Sidebar controller (cells, diagnostics, actions) |
plugin/calc/navigation.py |
Click-to-navigate from sidebar |
plugin/calc/excel_py_convert/ |
Excel Python-in-Excel → DAG =PY (auto on open + CLI) |
plugin/scripting/venv/editor_main.py |
Child process entry (runs in user venv) |
extension-core/registry/.../Sidebar.xcu |
LibrePyDeck + PythonPanel (Calc only); deck icon assets/python_32.png (PSF two-snakes) |
extension-core/registry/.../Factories.xcu |
PythonPanelFactory registration |
extension/Dialogs/PythonSidebarDialog.xdl |
Sidebar layout |
Requires Layer 2 (appearance.py, document_scripts.py, python_runner.py) and Layer 0 worker for Run from Monaco.
LibrePy Python sidebar: Calc-only native deck (not chat). Lists active-sheet =PY() cells, shows filtered diagnostics, and dispatches existing menu actions. Monaco remains a separate pywebview window.
| Asset | Location |
|---|---|
Monaco vs/, shell HTML/JS/CSS |
User venv rocher (via pip install rocher) |
| Dev reference copies | plugin/contrib/scripting/assets/editor/* |
uv pip install pywebview rocher PyQt6 PyQt6-WebEngine qtpyOptional: jedi (completion stub in editor_main.py).
Edit Python in Cell… does not fall back to embedded LO Python — fix the venv if Monaco fails to open.
Local OCR and document layout via trusted Vision helpers. Manual path first per image-recognition.md. Core ships Run Python Script → Vision Helpers + Vision OCR Settings — not chat extract_text_from_image.
flowchart LR
rps[Run Python Script Vision Helpers]
runner[vision_runner.py]
rpc[trusted RPC domain vision]
venv[vision/venv/vision.py]
egressW[writer/format.py HTML insert]
egressC[calc/vision_egress.py]
rps --> runner --> rpc --> venv
venv --> egressW
venv --> egressC
| File | Role |
|---|---|
vision_common.py |
Shared types/helpers |
vision_templates.py |
RPS script templates |
vision_runner.py |
Host orchestration, egress |
vision_egress.py |
Writer HTML insert routing |
vision_availability.py |
Stack gating |
module.yaml |
Settings schema → VisionSettingsDialog |
Omit vision_tools.py for menu-only core (LLM tool wrapper).
| File | Role |
|---|---|
vision.py |
run_vision dispatcher |
vision_docling.py |
Docling OCR/layout |
vision_paddle.py |
PaddleOCR fallback |
vision_html_export.py |
HTML prep for LO import |
vision_layout_html.py |
Structure HTML export |
Registered in trusted_action_registry.py as domain vision → trusted_dispatch.py.
| File | Role |
|---|---|
calc/vision_egress.py |
Calc HTML / structured grid insert |
calc/rich_html.py |
insert_cell_html_rich |
writer/format.py |
Writer HTML at cursor; run_writer_mutation_with_optional_review wraps insert in EditReviewSession when WriterAgent’s edit_review module is co-installed (LibrePy alone applies directly) |
doc/visual_helpers.py |
Export graphic to bytes |
| Resource | Role |
|---|---|
Menu vision.open_settings |
chatbot/module_config_dialog.py |
VisionSettingsDialog.xdl |
Generated from vision/module.yaml |
| Settings → Python Test | Vision Libraries probe in venv_diagnostics.py (omit Embeddings group in core build) |
uv pip install docling rapidocr-paddle numpy pillow css-inline
# optional fallback: paddleocr paddlepaddleModels are not bundled in the OXT; user venv installs them.
| Path | Role |
|---|---|
vision_tools.py |
extract_text_from_image LLM tool |
plugin/framework/tool.py |
Tool registration |
| Draw/Impress page-positioned OCR | Deferred — image-recognition.md Phase 1b.2 |