py-mre 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
py_mre-1.1.0/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Youngwoo Cho, Serin Chang
4
+
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
py_mre-1.1.0/PKG-INFO ADDED
@@ -0,0 +1,292 @@
1
+ Metadata-Version: 2.4
2
+ Name: py-mre
3
+ Version: 1.1.0
4
+ Summary: Machine-Readable Extension (MRE): generate LLM-navigable structured headers for HTML/HWPX/DOCX documents, for agentic RAG
5
+ Author-email: selyn-a <126850773+selyn-a@users.noreply.github.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Youngwoo Cho, Serin Chang
9
+
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
28
+
29
+ Project-URL: Homepage, https://github.com/Machine-Readable-Extension/mre
30
+ Project-URL: Repository, https://github.com/Machine-Readable-Extension/mre
31
+ Keywords: rag,retrieval-augmented-generation,llm-agent,mre,document-structure
32
+ Classifier: Development Status :: 5 - Production/Stable
33
+ Classifier: Intended Audience :: Developers
34
+ Classifier: Intended Audience :: Science/Research
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3.10
38
+ Classifier: Programming Language :: Python :: 3.11
39
+ Classifier: Programming Language :: Python :: 3.12
40
+ Classifier: Topic :: Text Processing :: Markup
41
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
42
+ Requires-Python: >=3.10
43
+ Description-Content-Type: text/markdown
44
+ License-File: LICENSE
45
+ Requires-Dist: openai>=1.0
46
+ Requires-Dist: beautifulsoup4>=4.9
47
+ Requires-Dist: lxml>=4.6
48
+ Requires-Dist: olefile>=0.46
49
+ Requires-Dist: pypdf>=4.0
50
+ Provides-Extra: fuzzy
51
+ Requires-Dist: thefuzz>=0.19; extra == "fuzzy"
52
+ Provides-Extra: test
53
+ Requires-Dist: pytest>=8.0; extra == "test"
54
+ Requires-Dist: pytest-asyncio>=0.23; extra == "test"
55
+ Requires-Dist: pytest-cov>=5.0; extra == "test"
56
+ Provides-Extra: docs
57
+ Requires-Dist: mkdocs-material>=9.5; extra == "docs"
58
+ Requires-Dist: mkdocstrings[python]>=0.24; extra == "docs"
59
+ Provides-Extra: typecheck
60
+ Requires-Dist: mypy>=1.8; extra == "typecheck"
61
+ Dynamic: license-file
62
+
63
+ # Machine-Readable-Extension(MRE)
64
+
65
+ [![Tests](https://github.com/Machine-Readable-Extension/mre/actions/workflows/tests.yml/badge.svg)](https://github.com/Machine-Readable-Extension/mre/actions/workflows/tests.yml)
66
+ [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=Machine-Readable-Extension_mre&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=Machine-Readable-Extension_mre)
67
+ [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=Machine-Readable-Extension_mre&metric=bugs)](https://sonarcloud.io/summary/new_code?id=Machine-Readable-Extension_mre)
68
+ [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Machine-Readable-Extension_mre&metric=coverage)](https://sonarcloud.io/summary/new_code?id=Machine-Readable-Extension_mre)
69
+ [![Docs](https://img.shields.io/badge/docs-machine--readable--extension.github.io-blue)](https://machine-readable-extension.github.io/mre/)
70
+
71
+ **Machine-Readable Extension (MRE)** — a producer-side document standard and
72
+ navigation structure that lets LLM agents read a document precisely, instead
73
+ of consuming its raw, markup-heavy source.
74
+
75
+ Full docs, including the auto-generated API reference: **[machine-readable-extension.github.io/mre](https://machine-readable-extension.github.io/mre/)**
76
+
77
+ ## Why
78
+
79
+ The web is built for human eyes: HTML pages are dominated by markup tags,
80
+ scripts, styling, and boilerplate that a browser renders away but an LLM
81
+ agent has to read in full. The same overhead shows up in XML-based formats
82
+ like DOCX, HWPX, and EPUB. That noise raises inference cost and increases the
83
+ risk of an agent missing the passage it actually needed, buried somewhere in
84
+ a long context.
85
+
86
+ MRE takes a different approach from *cleaning documents up after the fact*
87
+ (heuristic scrapers, LLM-written parsers per site). It puts a small,
88
+ standardized XML header into the document **once, at publication time** —
89
+ much like `robots.txt` or `sitemap.xml` let producers tell crawlers where to
90
+ go. The header doesn't just say what a document is about; it tells an agent
91
+ exactly which paragraph to look at, by a stable ID. An agent reads the
92
+ header, requests only the paragraphs it needs, and never has to touch the
93
+ raw markup.
94
+
95
+ This package covers both ends of that story for a registered site: it
96
+ generates and embeds MRE headers into a document (`generate_mre()`), and it
97
+ retrieves a specific paragraph's full text back out of an MRE-embedded
98
+ document by ID (`fetch_block()`) — the same per-site adapter backs both, so
99
+ a site's owner writes their parsing logic once and it stays correct on both
100
+ sides. Everything *above* that — the agent's turn-by-turn reasoning loop
101
+ deciding which IDs to request — is a separate concern, kept out of this
102
+ core package. An opt-in `mre.agent` subpackage implements one such loop for
103
+ anyone who wants it too, not just the primitives — see
104
+ [Agentic RAG](#agentic-rag), below.
105
+
106
+ ## What it looks like
107
+
108
+ ```xml
109
+ <html>
110
+ <head>
111
+ ...
112
+ <script type="application/mre+xml">
113
+ <mre version="1.0">
114
+ <metadata>
115
+ <title>Pleasure Cove</title>
116
+ <summary>Pleasure Cove is ...</summary>
117
+ </metadata>
118
+ <tree>
119
+ <node id="p1">
120
+ <desc>Released in 1979 on the ABC network.</desc>
121
+ <keys>Pleasure Cove, 1979, ABC</keys>
122
+ </node>
123
+ <node id="p2">
124
+ <desc>Protagonist is a conman at the resort.</desc>
125
+ <keys>Raymond Gordon, conman, resort</keys>
126
+ </node>
127
+ <node id="p3">
128
+ <desc>Tom Jones stars as Raymond Gordon.</desc>
129
+ <keys>Tom Jones, Raymond Gordon</keys>
130
+ </node>
131
+ </tree>
132
+ </mre>
133
+ </script>
134
+ </head>
135
+ <body>
136
+ ...
137
+ </body>
138
+ </html>
139
+ ```
140
+
141
+ `<metadata>` carries the document's title and a short summary — it lets an
142
+ agent judge whether the *whole document* is worth exploring before reading
143
+ anything else. `<tree>` maps the document's paragraphs: each `<node>` has a
144
+ stable ID, a `<desc>` naming what the paragraph asserts, and `<keys>` —
145
+ distinctive entity names that let an agent bridge across documents when
146
+ chasing a multi-hop query. Paragraph text itself is *not* in the header —
147
+ it's fetched on demand by ID from the source document, by a separate
148
+ retrieval-side parser. Full schema: [Specification](https://machine-readable-extension.github.io/mre/spec/).
149
+
150
+ Early experiments on MRE-based agentic RAG show up to a 48.4% relative F1
151
+ improvement over baselines on multi-hop QA benchmarks, by letting the agent
152
+ target the right paragraph instead of reading (or missing) it inside a
153
+ long, noisy context. Write-up is in progress (not yet on arXiv); benchmark
154
+ and evaluation code lives at
155
+ [Lactobacillus/machine-readable-extension](https://github.com/Lactobacillus/machine-readable-extension).
156
+
157
+ ## Install
158
+
159
+ ```bash
160
+ pip install py-mre
161
+ ```
162
+
163
+ The PyPI distribution is named `py-mre` (`mre` itself was already taken by an
164
+ unrelated project) — `import mre` either way.
165
+
166
+ The keyword-grounding [repair pass](https://machine-readable-extension.github.io/mre/quickstart/#repair)
167
+ has an optional fuzzy-matching fallback:
168
+
169
+ ```bash
170
+ pip install "py-mre[fuzzy]"
171
+ ```
172
+
173
+ ## Quick start
174
+
175
+ ```python
176
+ import asyncio
177
+ import openai
178
+ from mre import generate_mre
179
+
180
+ async def main():
181
+ # Any OpenAI-compatible async client works — the official SDK, or a
182
+ # vLLM / other OpenAI-compatible server (just point base_url at it).
183
+ client = openai.AsyncOpenAI()
184
+
185
+ with open("pleasure_cove.html", encoding="utf-8") as f:
186
+ html = f.read()
187
+
188
+ result = await generate_mre(
189
+ html,
190
+ client=client,
191
+ model="gpt-4o-mini",
192
+ title="Pleasure Cove",
193
+ url="https://en.wikipedia.org/wiki/Pleasure_Cove", # picks the site adapter
194
+ )
195
+
196
+ print(result.mre_xml) # the generated <mre> block
197
+ print(result.embedded_html) # original HTML with MRE injected into <head>
198
+
199
+ asyncio.run(main())
200
+ ```
201
+
202
+ `generate_mre()` auto-detects the document format from `fmt`/`url`/magic
203
+ bytes, dispatches to the right adapter, calls the LLM to fill in per-paragraph
204
+ `<desc>`/`<keys>` and a document `<summary>`, repairs any paragraphs the LLM
205
+ skipped, assembles the `<mre>` XML, and (by default) embeds it back into the
206
+ document. Model choice isn't hardcoded — you always pass your own
207
+ `(client, model)`.
208
+
209
+ ### Fetching a paragraph back out
210
+
211
+ An agent reading the header decides which `id`s it needs, then calls
212
+ `fetch_block()` (HTML only, for now) to get that paragraph's full text —
213
+ untruncated, unlike the short preview `<desc>` the LLM saw while generating:
214
+
215
+ ```python
216
+ from mre import fetch_block
217
+
218
+ text = fetch_block(
219
+ "https://en.wikipedia.org/wiki/Pleasure_Cove", # same url, to pick the adapter
220
+ result.embedded_html,
221
+ "p2",
222
+ )
223
+ ```
224
+
225
+ Pass `"full"` as the id to get the whole document's text at once, for a
226
+ workflow where the agent decides a single paragraph isn't enough context.
227
+ This goes through the *same* site adapter as generation — for a document
228
+ whose adapter doesn't implement `fetch` (a generation-only adapter),
229
+ `fetch_block()` raises `FetchNotSupportedError` rather than guessing.
230
+
231
+ `generate_mre()`/`fetch_block()` also guard against a document outliving the
232
+ adapter that generated it (a `generator-fingerprint` mismatch) — see
233
+ [Detecting a stale adapter](https://machine-readable-extension.github.io/mre/quickstart/#detecting-a-stale-adapter)
234
+ in the docs.
235
+
236
+ ## Agentic RAG
237
+
238
+ `mre` core stops at generating and fetching — deciding which `id`s to
239
+ request, turn by turn, is a separate concern kept out of the base package.
240
+ The opt-in `mre.agent` subpackage implements that loop, using **progressive
241
+ disclosure**: every candidate document starts out showing only its
242
+ `<metadata>`, and the agent expands specific documents' full `<tree>` (or
243
+ fetches them whole) before drilling into individual paragraphs. Candidate
244
+ documents can be HTML, HWPX, DOCX, or PDF, freely mixed in the same run.
245
+
246
+ ```python
247
+ from mre.agent import run_agent
248
+
249
+ result = await run_agent(
250
+ "Who starred in Pleasure Cove?",
251
+ {"Pleasure Cove": {"html": embedded_html, "url": "https://en.wikipedia.org/wiki/Pleasure_Cove"}},
252
+ client=client,
253
+ model="gpt-4o-mini",
254
+ )
255
+ print(result.answer, result.success, result.stats)
256
+ ```
257
+
258
+ Every piece `run_agent()` is built from — the system prompt, the guided-
259
+ decoding schemas, the metadata view — is independently importable too, for
260
+ wiring MRE into a different agent loop instead. Full walkthrough:
261
+ [Agentic RAG](https://machine-readable-extension.github.io/mre/agentic-rag/).
262
+
263
+ ## Supported document formats
264
+
265
+ | Format | Parsing | Embedding | Fetch |
266
+ |---|---|---|---|
267
+ | HTML (Wikipedia) | built-in site adapter | `<script type="application/mre+xml">` inside `<head>` | `fetch_block()` |
268
+ | HWPX | built-in | extra `mre.xml` entry in the zip archive | `fetch_opc()` |
269
+ | DOCX | built-in (body paragraphs only — table cells are out of scope) | extra `mre.xml` entry in the zip archive | `fetch_opc()` |
270
+ | HWP (legacy, OLE2) | built-in, parsing-only — `mre.hwp_adapter.parse_hwp()` | not implemented | not yet |
271
+ | PDF | built-in, paragraph text only — see [Legacy HWP / PDF](https://machine-readable-extension.github.io/mre/formats/) | `mre.xml` as a PDF file attachment | `fetch_pdf()` |
272
+
273
+ > ⚠️ **HWP has no embed path, and the one workaround has measured content loss.**
274
+ > `mre.convert_hwp(path, target=DocFormat.DOCX)` shells out to an externally-installed
275
+ > LibreOffice + [H2Orestart](https://github.com/ebandal/H2Orestart) (a community
276
+ > reverse-engineered filter, not Hancom's own converter) so you can embed into the
277
+ > converted docx/pdf instead. A revision-history entry vanished entirely in testing
278
+ > against a real government document — verify important documents' output before
279
+ > trusting it. See [Legacy HWP](https://machine-readable-extension.github.io/mre/formats/#legacy-hwp-parsing-only)
280
+ > for the full fidelity numbers and usage.
281
+
282
+ HTML support is a **site-adapter registry**, not a generic scraper — only
283
+ `wikipedia.org` ships out of the box, but a new site can be registered
284
+ in-process or shipped as an installable plugin package (an `mre.site_adapters`
285
+ entry point). See [`examples/mre-example-adapter/`](examples/mre-example-adapter)
286
+ for a working reference, and
287
+ [Document formats](https://machine-readable-extension.github.io/mre/formats/)
288
+ for the full guide to adding a site.
289
+
290
+ ## License
291
+
292
+ MIT — see [LICENSE](LICENSE).
py_mre-1.1.0/README.md ADDED
@@ -0,0 +1,230 @@
1
+ # Machine-Readable-Extension(MRE)
2
+
3
+ [![Tests](https://github.com/Machine-Readable-Extension/mre/actions/workflows/tests.yml/badge.svg)](https://github.com/Machine-Readable-Extension/mre/actions/workflows/tests.yml)
4
+ [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=Machine-Readable-Extension_mre&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=Machine-Readable-Extension_mre)
5
+ [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=Machine-Readable-Extension_mre&metric=bugs)](https://sonarcloud.io/summary/new_code?id=Machine-Readable-Extension_mre)
6
+ [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Machine-Readable-Extension_mre&metric=coverage)](https://sonarcloud.io/summary/new_code?id=Machine-Readable-Extension_mre)
7
+ [![Docs](https://img.shields.io/badge/docs-machine--readable--extension.github.io-blue)](https://machine-readable-extension.github.io/mre/)
8
+
9
+ **Machine-Readable Extension (MRE)** — a producer-side document standard and
10
+ navigation structure that lets LLM agents read a document precisely, instead
11
+ of consuming its raw, markup-heavy source.
12
+
13
+ Full docs, including the auto-generated API reference: **[machine-readable-extension.github.io/mre](https://machine-readable-extension.github.io/mre/)**
14
+
15
+ ## Why
16
+
17
+ The web is built for human eyes: HTML pages are dominated by markup tags,
18
+ scripts, styling, and boilerplate that a browser renders away but an LLM
19
+ agent has to read in full. The same overhead shows up in XML-based formats
20
+ like DOCX, HWPX, and EPUB. That noise raises inference cost and increases the
21
+ risk of an agent missing the passage it actually needed, buried somewhere in
22
+ a long context.
23
+
24
+ MRE takes a different approach from *cleaning documents up after the fact*
25
+ (heuristic scrapers, LLM-written parsers per site). It puts a small,
26
+ standardized XML header into the document **once, at publication time** —
27
+ much like `robots.txt` or `sitemap.xml` let producers tell crawlers where to
28
+ go. The header doesn't just say what a document is about; it tells an agent
29
+ exactly which paragraph to look at, by a stable ID. An agent reads the
30
+ header, requests only the paragraphs it needs, and never has to touch the
31
+ raw markup.
32
+
33
+ This package covers both ends of that story for a registered site: it
34
+ generates and embeds MRE headers into a document (`generate_mre()`), and it
35
+ retrieves a specific paragraph's full text back out of an MRE-embedded
36
+ document by ID (`fetch_block()`) — the same per-site adapter backs both, so
37
+ a site's owner writes their parsing logic once and it stays correct on both
38
+ sides. Everything *above* that — the agent's turn-by-turn reasoning loop
39
+ deciding which IDs to request — is a separate concern, kept out of this
40
+ core package. An opt-in `mre.agent` subpackage implements one such loop for
41
+ anyone who wants it too, not just the primitives — see
42
+ [Agentic RAG](#agentic-rag), below.
43
+
44
+ ## What it looks like
45
+
46
+ ```xml
47
+ <html>
48
+ <head>
49
+ ...
50
+ <script type="application/mre+xml">
51
+ <mre version="1.0">
52
+ <metadata>
53
+ <title>Pleasure Cove</title>
54
+ <summary>Pleasure Cove is ...</summary>
55
+ </metadata>
56
+ <tree>
57
+ <node id="p1">
58
+ <desc>Released in 1979 on the ABC network.</desc>
59
+ <keys>Pleasure Cove, 1979, ABC</keys>
60
+ </node>
61
+ <node id="p2">
62
+ <desc>Protagonist is a conman at the resort.</desc>
63
+ <keys>Raymond Gordon, conman, resort</keys>
64
+ </node>
65
+ <node id="p3">
66
+ <desc>Tom Jones stars as Raymond Gordon.</desc>
67
+ <keys>Tom Jones, Raymond Gordon</keys>
68
+ </node>
69
+ </tree>
70
+ </mre>
71
+ </script>
72
+ </head>
73
+ <body>
74
+ ...
75
+ </body>
76
+ </html>
77
+ ```
78
+
79
+ `<metadata>` carries the document's title and a short summary — it lets an
80
+ agent judge whether the *whole document* is worth exploring before reading
81
+ anything else. `<tree>` maps the document's paragraphs: each `<node>` has a
82
+ stable ID, a `<desc>` naming what the paragraph asserts, and `<keys>` —
83
+ distinctive entity names that let an agent bridge across documents when
84
+ chasing a multi-hop query. Paragraph text itself is *not* in the header —
85
+ it's fetched on demand by ID from the source document, by a separate
86
+ retrieval-side parser. Full schema: [Specification](https://machine-readable-extension.github.io/mre/spec/).
87
+
88
+ Early experiments on MRE-based agentic RAG show up to a 48.4% relative F1
89
+ improvement over baselines on multi-hop QA benchmarks, by letting the agent
90
+ target the right paragraph instead of reading (or missing) it inside a
91
+ long, noisy context. Write-up is in progress (not yet on arXiv); benchmark
92
+ and evaluation code lives at
93
+ [Lactobacillus/machine-readable-extension](https://github.com/Lactobacillus/machine-readable-extension).
94
+
95
+ ## Install
96
+
97
+ ```bash
98
+ pip install py-mre
99
+ ```
100
+
101
+ The PyPI distribution is named `py-mre` (`mre` itself was already taken by an
102
+ unrelated project) — `import mre` either way.
103
+
104
+ The keyword-grounding [repair pass](https://machine-readable-extension.github.io/mre/quickstart/#repair)
105
+ has an optional fuzzy-matching fallback:
106
+
107
+ ```bash
108
+ pip install "py-mre[fuzzy]"
109
+ ```
110
+
111
+ ## Quick start
112
+
113
+ ```python
114
+ import asyncio
115
+ import openai
116
+ from mre import generate_mre
117
+
118
+ async def main():
119
+ # Any OpenAI-compatible async client works — the official SDK, or a
120
+ # vLLM / other OpenAI-compatible server (just point base_url at it).
121
+ client = openai.AsyncOpenAI()
122
+
123
+ with open("pleasure_cove.html", encoding="utf-8") as f:
124
+ html = f.read()
125
+
126
+ result = await generate_mre(
127
+ html,
128
+ client=client,
129
+ model="gpt-4o-mini",
130
+ title="Pleasure Cove",
131
+ url="https://en.wikipedia.org/wiki/Pleasure_Cove", # picks the site adapter
132
+ )
133
+
134
+ print(result.mre_xml) # the generated <mre> block
135
+ print(result.embedded_html) # original HTML with MRE injected into <head>
136
+
137
+ asyncio.run(main())
138
+ ```
139
+
140
+ `generate_mre()` auto-detects the document format from `fmt`/`url`/magic
141
+ bytes, dispatches to the right adapter, calls the LLM to fill in per-paragraph
142
+ `<desc>`/`<keys>` and a document `<summary>`, repairs any paragraphs the LLM
143
+ skipped, assembles the `<mre>` XML, and (by default) embeds it back into the
144
+ document. Model choice isn't hardcoded — you always pass your own
145
+ `(client, model)`.
146
+
147
+ ### Fetching a paragraph back out
148
+
149
+ An agent reading the header decides which `id`s it needs, then calls
150
+ `fetch_block()` (HTML only, for now) to get that paragraph's full text —
151
+ untruncated, unlike the short preview `<desc>` the LLM saw while generating:
152
+
153
+ ```python
154
+ from mre import fetch_block
155
+
156
+ text = fetch_block(
157
+ "https://en.wikipedia.org/wiki/Pleasure_Cove", # same url, to pick the adapter
158
+ result.embedded_html,
159
+ "p2",
160
+ )
161
+ ```
162
+
163
+ Pass `"full"` as the id to get the whole document's text at once, for a
164
+ workflow where the agent decides a single paragraph isn't enough context.
165
+ This goes through the *same* site adapter as generation — for a document
166
+ whose adapter doesn't implement `fetch` (a generation-only adapter),
167
+ `fetch_block()` raises `FetchNotSupportedError` rather than guessing.
168
+
169
+ `generate_mre()`/`fetch_block()` also guard against a document outliving the
170
+ adapter that generated it (a `generator-fingerprint` mismatch) — see
171
+ [Detecting a stale adapter](https://machine-readable-extension.github.io/mre/quickstart/#detecting-a-stale-adapter)
172
+ in the docs.
173
+
174
+ ## Agentic RAG
175
+
176
+ `mre` core stops at generating and fetching — deciding which `id`s to
177
+ request, turn by turn, is a separate concern kept out of the base package.
178
+ The opt-in `mre.agent` subpackage implements that loop, using **progressive
179
+ disclosure**: every candidate document starts out showing only its
180
+ `<metadata>`, and the agent expands specific documents' full `<tree>` (or
181
+ fetches them whole) before drilling into individual paragraphs. Candidate
182
+ documents can be HTML, HWPX, DOCX, or PDF, freely mixed in the same run.
183
+
184
+ ```python
185
+ from mre.agent import run_agent
186
+
187
+ result = await run_agent(
188
+ "Who starred in Pleasure Cove?",
189
+ {"Pleasure Cove": {"html": embedded_html, "url": "https://en.wikipedia.org/wiki/Pleasure_Cove"}},
190
+ client=client,
191
+ model="gpt-4o-mini",
192
+ )
193
+ print(result.answer, result.success, result.stats)
194
+ ```
195
+
196
+ Every piece `run_agent()` is built from — the system prompt, the guided-
197
+ decoding schemas, the metadata view — is independently importable too, for
198
+ wiring MRE into a different agent loop instead. Full walkthrough:
199
+ [Agentic RAG](https://machine-readable-extension.github.io/mre/agentic-rag/).
200
+
201
+ ## Supported document formats
202
+
203
+ | Format | Parsing | Embedding | Fetch |
204
+ |---|---|---|---|
205
+ | HTML (Wikipedia) | built-in site adapter | `<script type="application/mre+xml">` inside `<head>` | `fetch_block()` |
206
+ | HWPX | built-in | extra `mre.xml` entry in the zip archive | `fetch_opc()` |
207
+ | DOCX | built-in (body paragraphs only — table cells are out of scope) | extra `mre.xml` entry in the zip archive | `fetch_opc()` |
208
+ | HWP (legacy, OLE2) | built-in, parsing-only — `mre.hwp_adapter.parse_hwp()` | not implemented | not yet |
209
+ | PDF | built-in, paragraph text only — see [Legacy HWP / PDF](https://machine-readable-extension.github.io/mre/formats/) | `mre.xml` as a PDF file attachment | `fetch_pdf()` |
210
+
211
+ > ⚠️ **HWP has no embed path, and the one workaround has measured content loss.**
212
+ > `mre.convert_hwp(path, target=DocFormat.DOCX)` shells out to an externally-installed
213
+ > LibreOffice + [H2Orestart](https://github.com/ebandal/H2Orestart) (a community
214
+ > reverse-engineered filter, not Hancom's own converter) so you can embed into the
215
+ > converted docx/pdf instead. A revision-history entry vanished entirely in testing
216
+ > against a real government document — verify important documents' output before
217
+ > trusting it. See [Legacy HWP](https://machine-readable-extension.github.io/mre/formats/#legacy-hwp-parsing-only)
218
+ > for the full fidelity numbers and usage.
219
+
220
+ HTML support is a **site-adapter registry**, not a generic scraper — only
221
+ `wikipedia.org` ships out of the box, but a new site can be registered
222
+ in-process or shipped as an installable plugin package (an `mre.site_adapters`
223
+ entry point). See [`examples/mre-example-adapter/`](examples/mre-example-adapter)
224
+ for a working reference, and
225
+ [Document formats](https://machine-readable-extension.github.io/mre/formats/)
226
+ for the full guide to adding a site.
227
+
228
+ ## License
229
+
230
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,82 @@
1
+ __version__ = "1.1.0"
2
+
3
+ from mre.format_detect import DocFormat, FormatDetectionError, detect_format
4
+ from mre.html_site_adapter import (
5
+ FetchNotSupportedError,
6
+ GeneratorFingerprintMismatch,
7
+ HTMLSiteAdapter,
8
+ UnknownSiteError,
9
+ compute_adapter_fingerprint,
10
+ detect_site,
11
+ discover_plugin_adapters,
12
+ fetch_block,
13
+ get_site_adapter,
14
+ parse_html,
15
+ register_site,
16
+ registered_sites,
17
+ )
18
+ from mre.opc_adapter import (
19
+ OPCAdapter,
20
+ embed_mre_opc,
21
+ extract_mre_xml_opc,
22
+ fetch_opc,
23
+ get_opc_adapter,
24
+ parse_opc,
25
+ )
26
+ from mre.generate import MREGenerationResult, generate_mre
27
+ from mre.hwp_adapter import parse_hwp
28
+ from mre.hwp_convert import (
29
+ HwpConversionError,
30
+ LibreOfficeNotAvailableError,
31
+ convert_hwp,
32
+ )
33
+ from mre.pdf_adapter import (
34
+ embed_mre_pdf,
35
+ extract_mre_xml_pdf,
36
+ fetch_pdf,
37
+ mre_xml_exists_pdf,
38
+ parse_pdf,
39
+ )
40
+ from mre.reader import extract_mre_xml
41
+
42
+ # 내장 어댑터(Wikipedia 등)는 mre.html_site_adapter 모듈 로드 시 이미 등록됐다. 플러그인
43
+ # 발견은 여기, mre 패키지의 다른 모든 export 가 이미 바인딩된 뒤에 실행해야 한다 — 플러그인이
44
+ # 관례대로 `from mre import HTMLSiteAdapter` 로 임포트할 때 순환 임포트가 나지 않도록.
45
+ discover_plugin_adapters()
46
+
47
+ __all__ = [
48
+ "__version__",
49
+ "DocFormat",
50
+ "FormatDetectionError",
51
+ "detect_format",
52
+ "FetchNotSupportedError",
53
+ "GeneratorFingerprintMismatch",
54
+ "HTMLSiteAdapter",
55
+ "UnknownSiteError",
56
+ "compute_adapter_fingerprint",
57
+ "detect_site",
58
+ "discover_plugin_adapters",
59
+ "fetch_block",
60
+ "get_site_adapter",
61
+ "parse_html",
62
+ "register_site",
63
+ "registered_sites",
64
+ "OPCAdapter",
65
+ "embed_mre_opc",
66
+ "extract_mre_xml_opc",
67
+ "fetch_opc",
68
+ "get_opc_adapter",
69
+ "parse_opc",
70
+ "MREGenerationResult",
71
+ "generate_mre",
72
+ "parse_hwp",
73
+ "HwpConversionError",
74
+ "LibreOfficeNotAvailableError",
75
+ "convert_hwp",
76
+ "embed_mre_pdf",
77
+ "extract_mre_xml_pdf",
78
+ "fetch_pdf",
79
+ "mre_xml_exists_pdf",
80
+ "parse_pdf",
81
+ "extract_mre_xml",
82
+ ]
@@ -0,0 +1,41 @@
1
+ """
2
+ mre.agent — MRE 기반 agentic RAG 루프, 옵트인 서브패키지.
3
+
4
+ `mre` 코어 패키지는 문서 표준(생성 + fetch)만 다루는 게 의도적인 경계다 —
5
+ "에이전트의 턴별 추론 루프는 이 패키지의 관심사가 아니다"(mre/README.md). 이 서브패키지는
6
+ 그 경계 바깥, 실제로 MRE 헤더를 읽고 도구를 호출하며 답을 도출하는 루프를 원하는 사용자를
7
+ 위한 것 — 코어를 건드리지 않고 독립적으로 버저닝/실험한다.
8
+
9
+ 지금은 progressive(metadata-only 2단계 공개) 방식 하나만 구현한다. 두 가지 방식으로
10
+ 쓸 수 있다:
11
+ 1. run_agent() 하나로 바로 — 완성형 진입점.
12
+ 2. 개별 조각(build_progressive_action_schema/metadata_view/SYSTEM_PROMPT 등)을 가져다
13
+ 직접 다른 루프(LangChain 등)에 도구로 꽂아 쓰기 — mre 코어가 generate_mre() 와
14
+ HTMLSiteAdapter/fetch_block/build_mre_xml 을 둘 다 공개하는 것과 동일한 패턴.
15
+ """
16
+
17
+ from mre.agent.loop import AgentResult, BlockFetchError, MRENotFoundError, run_agent
18
+ from mre.agent.prompts import ANSWER_FORMAT, SYSTEM_PROMPT
19
+ from mre.agent.schema import (
20
+ CHECK_SUFFICIENCY_SCHEMA,
21
+ MAX_DOCS_PER_TURN,
22
+ MAX_PIDS_PER_DOC,
23
+ MAX_TURNS,
24
+ build_progressive_action_schema,
25
+ )
26
+ from mre.agent.views import metadata_view
27
+
28
+ __all__ = [
29
+ "AgentResult",
30
+ "BlockFetchError",
31
+ "MRENotFoundError",
32
+ "run_agent",
33
+ "ANSWER_FORMAT",
34
+ "SYSTEM_PROMPT",
35
+ "CHECK_SUFFICIENCY_SCHEMA",
36
+ "MAX_DOCS_PER_TURN",
37
+ "MAX_PIDS_PER_DOC",
38
+ "MAX_TURNS",
39
+ "build_progressive_action_schema",
40
+ "metadata_view",
41
+ ]