kordoc 0.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.
- kordoc-0.1.0/LICENSE +21 -0
- kordoc-0.1.0/PKG-INFO +143 -0
- kordoc-0.1.0/README.md +122 -0
- kordoc-0.1.0/pyproject.toml +37 -0
- kordoc-0.1.0/setup.cfg +4 -0
- kordoc-0.1.0/src/kordoc/__init__.py +34 -0
- kordoc-0.1.0/src/kordoc/_client.py +365 -0
- kordoc-0.1.0/src/kordoc/_config.py +93 -0
- kordoc-0.1.0/src/kordoc/_errors.py +53 -0
- kordoc-0.1.0/src/kordoc/_protocol.py +73 -0
- kordoc-0.1.0/src/kordoc/_result.py +115 -0
- kordoc-0.1.0/src/kordoc/_worker.py +172 -0
- kordoc-0.1.0/src/kordoc/py.typed +0 -0
- kordoc-0.1.0/src/kordoc.egg-info/PKG-INFO +143 -0
- kordoc-0.1.0/src/kordoc.egg-info/SOURCES.txt +20 -0
- kordoc-0.1.0/src/kordoc.egg-info/dependency_links.txt +1 -0
- kordoc-0.1.0/src/kordoc.egg-info/requires.txt +4 -0
- kordoc-0.1.0/src/kordoc.egg-info/top_level.txt +1 -0
- kordoc-0.1.0/tests/test_contract.py +53 -0
- kordoc-0.1.0/tests/test_engine.py +126 -0
- kordoc-0.1.0/tests/test_faults.py +121 -0
- kordoc-0.1.0/tests/test_lifecycle.py +137 -0
kordoc-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 chrisryugj
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
kordoc-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kordoc
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for kordoc — parse Korean documents (HWP, HWPX, PDF, DOCX, XLSX …) through a resident kordoc engine worker
|
|
5
|
+
Author: chrisryugj, Byeory Jeong
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/chrisryugj/kordoc
|
|
8
|
+
Project-URL: Source, https://github.com/chrisryugj/kordoc/tree/main/sdk/python
|
|
9
|
+
Keywords: hwp,hwpx,korean,document,parser,markdown,rag
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Topic :: Text Processing :: Markup
|
|
14
|
+
Requires-Python: >=3.11
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Provides-Extra: test
|
|
18
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
19
|
+
Requires-Dist: build>=1.2; extra == "test"
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# kordoc Python SDK
|
|
23
|
+
|
|
24
|
+
Python 에서 kordoc 으로 한국 문서(HWP·HWPX·PDF·DOCX·XLSX·PPTX …)를 파싱합니다.
|
|
25
|
+
상주 kordoc 엔진 워커(`kordoc parse-worker --protocol 2`)를 띄워 재사용하므로 문서마다 Node 를 새로 실행하지 않습니다.
|
|
26
|
+
파싱은 엔진 한 곳에서 하며 결과는 Node `parse()` 와 같습니다.
|
|
27
|
+
|
|
28
|
+
## 설치
|
|
29
|
+
|
|
30
|
+
이 패키지는 엔진을 설치하지 않습니다. 실행 환경에 Node.js(20 이상)와 kordoc 엔진이 따로 있어야 합니다.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm i -g kordoc # 엔진 (PATH 에 kordoc)
|
|
34
|
+
pip install kordoc # SDK (Python 3.11 이상, 외부 의존성 없음)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
엔진 위치는 `KordocConfig(node=..., cli=...)`, 환경변수 `KORDOC_NODE`·`KORDOC_CLI`, PATH 의 `node`·`kordoc` 순으로 찾습니다.
|
|
38
|
+
`cli` 에는 엔진의 `dist/cli.js`(또는 npm 이 만든 `kordoc` 링크)를 줍니다. 실행 인자는 배열로 넘기며 shell·`npx` 를 쓰지 않습니다.
|
|
39
|
+
Windows 의 `kordoc.cmd`·pnpm 셸 shim 처럼 node 로 실행할 수 없는 래퍼를 찾으면 옆의 `node_modules/kordoc/dist/cli.js` 로 바꿔 쓰고, 그것도 없으면 `dist/cli.js` 를 지정하라는 오류를 냅니다.
|
|
40
|
+
|
|
41
|
+
폐쇄망에서는 [오프라인 번들](../../docs/offline-deployment.md)을 풀고 그 안의 엔진을 가리키면 됩니다.
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from kordoc import KordocClient, KordocConfig
|
|
45
|
+
|
|
46
|
+
config = KordocConfig(
|
|
47
|
+
node="/opt/node/bin/node",
|
|
48
|
+
cli="/opt/kordoc-offline/node_modules/kordoc/dist/cli.js",
|
|
49
|
+
env={"KORDOC_OFFLINE": "1"},
|
|
50
|
+
)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 사용
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from kordoc import KordocClient
|
|
57
|
+
|
|
58
|
+
with KordocClient() as client:
|
|
59
|
+
result = client.parse("문서.hwpx", table_format="gfm") # 중첩 표를 부모·자식 트리로 편 GFM
|
|
60
|
+
if result.success:
|
|
61
|
+
print(result.markdown)
|
|
62
|
+
else:
|
|
63
|
+
print(result.code, result.error) # 문서를 읽지 못한 것은 예외가 아니다
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
import asyncio
|
|
68
|
+
from kordoc import AsyncKordocClient
|
|
69
|
+
|
|
70
|
+
async def main() -> None:
|
|
71
|
+
async with AsyncKordocClient(max_workers=2) as client:
|
|
72
|
+
results = await asyncio.gather(*(client.parse(p) for p in ["a.hwp", "b.pdf"]))
|
|
73
|
+
|
|
74
|
+
asyncio.run(main())
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
- `parse(path, **options)` · `parse_bytes(data, **options)` · `warmup(path, **options)` · `close()`(`aclose()`)
|
|
78
|
+
- `with` / `async with` 를 나가면 소유 워커를 모두 종료·회수합니다. 패키지 import·클라이언트 생성만으로는 프로세스를 띄우지 않습니다.
|
|
79
|
+
- `result.raw` 는 엔진 결과 JSON 원본입니다(알 수 없는 필드 포함). `markdown`·`blocks`·`pages`·`metadata`·`warnings`·`images` 를 꺼내 쓸 수 있습니다.
|
|
80
|
+
- `result.raise_for_error()` 는 파싱 실패를 `KordocParseFailed` 로 바꿉니다.
|
|
81
|
+
|
|
82
|
+
### 파싱 옵션
|
|
83
|
+
|
|
84
|
+
키워드 인자로 줍니다. 빼면 엔진 기본값이고, `False` 를 주면 실제로 끕니다(`ocr=False` 는 OCR 끔).
|
|
85
|
+
|
|
86
|
+
`table_format`(`"gfm"`) · `html_tables` · `layout_tables`(`"visual"`·`"keep"`) · `classify_tables` · `tables` · `plain` · `script_tags` ·
|
|
87
|
+
`remove_header_footer` · `dedupe_running_headers` · `keep_trailing_empty_cols` · `keep_empty_paragraphs` · `include_field_placeholders` ·
|
|
88
|
+
`pages`(`"1-3"` 또는 `[1, 3]`) · `images` · `inline_images` · `ocr`(`True`·`False`·`"force"`) · `formula_ocr` · `password`
|
|
89
|
+
|
|
90
|
+
함수 값(OCR 프로바이더, 진행 콜백)과 입력 경로를 바꾸는 옵션은 받지 않습니다. 잘못된 옵션은 `KordocProtocolError`(`code="INVALID_OPTIONS"`)입니다.
|
|
91
|
+
|
|
92
|
+
### 이미지
|
|
93
|
+
|
|
94
|
+
| 방식 | 사용 | 동작 |
|
|
95
|
+
| --- | --- | --- |
|
|
96
|
+
| inline (기본) | `parse(p)` | 이미지 바이트를 결과 JSON 에 base64 로 받습니다 |
|
|
97
|
+
| files | `parse(p, image_transport="files", assets_dir="/data/assets")` | `assets_dir` 아래 요청마다 새 디렉터리에 파일로 받습니다 |
|
|
98
|
+
|
|
99
|
+
`result.images[i].read()` 를 부를 때만 바이트를 해독하거나 파일을 읽습니다. `filename` 은 Markdown 이 가리키는 이름, `path` 는 files 방식의 실제 파일입니다.
|
|
100
|
+
files 방식의 파일은 호출자 소유이며 클라이언트를 닫아도 남습니다. 이미지가 많은 문서는 files 방식이 응답을 작게 유지합니다.
|
|
101
|
+
제한 시간·취소로 실행 중인 워커를 종료하면 그 요청이 쓰던 `assets_dir/kordoc-<id>-*` 디렉터리가 남을 수 있습니다 — `assets_dir` 정리는 호출자가 합니다.
|
|
102
|
+
`images=False` 는 이미지 바이트 추출 자체를 생략하는 엔진 옵션으로, 전송 방식과 별개입니다.
|
|
103
|
+
|
|
104
|
+
## 워커 수명
|
|
105
|
+
|
|
106
|
+
| 설정 (`KordocConfig`) | 기본 | 동작 |
|
|
107
|
+
| --- | --- | --- |
|
|
108
|
+
| `max_workers` | 1 | 상주 워커 수. 워커 하나는 한 번에 요청 하나만 처리합니다. 코어 수로 자동으로 늘리지 않습니다 |
|
|
109
|
+
| `max_queue` | 64 | 워커를 기다리는 요청 수 상한. 넘으면 `KordocQueueFull` |
|
|
110
|
+
| `request_timeout` | 없음 | 요청 제한 시간(초). 대기 큐 입장부터 결과 수신까지 |
|
|
111
|
+
| `start_timeout` | 30 | 워커가 ready 를 보낼 때까지 |
|
|
112
|
+
| `warmup_timeout` | 300 | 워밍업 문서 한 건 |
|
|
113
|
+
| `close_timeout` | 10 | 닫을 때 진행 중 요청을 기다리는 시간 |
|
|
114
|
+
| `max_worker_rss_bytes` | 끔 | 응답의 rss 가 넘으면 그 워커를 다음 작업 전에 새 워커로 바꿉니다 |
|
|
115
|
+
|
|
116
|
+
- **제한 시간·취소**: 대기 중인 요청은 큐에서만 빠집니다. 실행 중인 요청은 담당 워커를 즉시 종료·회수합니다(엔진 파싱은 중간에 멈출 수 없어서입니다). 다음 요청은 새 워커가 받습니다. 늦게 온 응답이 다른 요청에 섞이지 않습니다. 비동기에서는 Task 취소가 같은 규칙을 따릅니다.
|
|
117
|
+
- **장애**: 워커가 끝나거나 응답이 깨지면 그 요청은 `KordocWorkerCrashed` 로 끝나고 자동 재시도하지 않습니다. 다음 요청에서 새 워커를 띄웁니다.
|
|
118
|
+
- **RSS 교체**: `max_worker_rss_bytes` 를 켜면 작업이 끝난 뒤 응답의 rss(완료 시점 값, 처리 중 최댓값 아님)를 보고 교체합니다. N 건마다 재시작하지 않습니다. 풀 메모리는 `worker_info()` 의 rss 합으로 봅니다. Node heap 상한은 별도로 `env={"NODE_OPTIONS": "--max-old-space-size=4096"}` 처럼 정합니다.
|
|
119
|
+
- **워밍업**: `warmup(대표 문서, **options)` 은 모든 워커에서 그 문서를 실제로 파싱하고, 교체된 워커도 요청을 받기 전에 같은 문서로 워밍업합니다. 결과(`WarmupReport`)의 경고는 그대로 전달합니다. OCR 을 건너뛴 경고가 있으면 OCR 준비가 된 것이 아닙니다. 워밍업은 처리 경로를 한 번 지나게 할 뿐 최적화 완료를 보장하지 않습니다.
|
|
120
|
+
- **닫기**: 새 요청을 막고, 대기 요청은 `KordocClosed` 로 끝내고, 진행 요청은 `close_timeout` 까지 기다린 뒤 워커를 종료합니다.
|
|
121
|
+
|
|
122
|
+
## 바이트 입력
|
|
123
|
+
|
|
124
|
+
`parse_bytes(data)` 는 바이트를 SDK 소유 임시 디렉터리(`temp_dir`, 기본 시스템 임시 폴더 아래 `kordoc-sdk-*`)에 파일로 쓴 뒤 파싱합니다.
|
|
125
|
+
큰 입력을 NDJSON 에 다시 싣지 않기 위해서이며 임시 파일 I/O 가 생깁니다. 성공·실패·취소와 관계없이 끝나면 지우고, 닫을 때 디렉터리도 지웁니다.
|
|
126
|
+
원본 파일 경로가 필요한 DRM 문서 대체 경로는 바이트 입력에서 파일 입력과 같게 동작하지 않을 수 있습니다.
|
|
127
|
+
|
|
128
|
+
## 그 밖에
|
|
129
|
+
|
|
130
|
+
- 워커 환경은 현재 환경을 물려받고 `env` 를 덧붙입니다(`KORDOC_OFFLINE`, `KORDOC_MODEL_CACHE` 등). SDK 는 요청 본문·암호를 로그에 남기지 않습니다.
|
|
131
|
+
- `KORDOC_ROOT` 는 MCP 서버의 접근 제한입니다. SDK 는 같은 샌드박스를 제공하지 않으며, 넘긴 경로를 그대로 읽습니다.
|
|
132
|
+
- 워커 프로토콜: [docs/parse-worker-protocol.md](../../docs/parse-worker-protocol.md)
|
|
133
|
+
|
|
134
|
+
## 개발
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npm ci && npm run build # 저장소 루트 — 엔진(dist/cli.js)
|
|
138
|
+
cd sdk/python
|
|
139
|
+
python -m venv .venv && . .venv/bin/activate
|
|
140
|
+
python -m pip install -e '.[test]'
|
|
141
|
+
python -m pytest # 실제 엔진 + 합성 문서(tests/fixtures/sdk-docs.ts) + 장애 주입 워커
|
|
142
|
+
python -m build
|
|
143
|
+
```
|
kordoc-0.1.0/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# kordoc Python SDK
|
|
2
|
+
|
|
3
|
+
Python 에서 kordoc 으로 한국 문서(HWP·HWPX·PDF·DOCX·XLSX·PPTX …)를 파싱합니다.
|
|
4
|
+
상주 kordoc 엔진 워커(`kordoc parse-worker --protocol 2`)를 띄워 재사용하므로 문서마다 Node 를 새로 실행하지 않습니다.
|
|
5
|
+
파싱은 엔진 한 곳에서 하며 결과는 Node `parse()` 와 같습니다.
|
|
6
|
+
|
|
7
|
+
## 설치
|
|
8
|
+
|
|
9
|
+
이 패키지는 엔진을 설치하지 않습니다. 실행 환경에 Node.js(20 이상)와 kordoc 엔진이 따로 있어야 합니다.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm i -g kordoc # 엔진 (PATH 에 kordoc)
|
|
13
|
+
pip install kordoc # SDK (Python 3.11 이상, 외부 의존성 없음)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
엔진 위치는 `KordocConfig(node=..., cli=...)`, 환경변수 `KORDOC_NODE`·`KORDOC_CLI`, PATH 의 `node`·`kordoc` 순으로 찾습니다.
|
|
17
|
+
`cli` 에는 엔진의 `dist/cli.js`(또는 npm 이 만든 `kordoc` 링크)를 줍니다. 실행 인자는 배열로 넘기며 shell·`npx` 를 쓰지 않습니다.
|
|
18
|
+
Windows 의 `kordoc.cmd`·pnpm 셸 shim 처럼 node 로 실행할 수 없는 래퍼를 찾으면 옆의 `node_modules/kordoc/dist/cli.js` 로 바꿔 쓰고, 그것도 없으면 `dist/cli.js` 를 지정하라는 오류를 냅니다.
|
|
19
|
+
|
|
20
|
+
폐쇄망에서는 [오프라인 번들](../../docs/offline-deployment.md)을 풀고 그 안의 엔진을 가리키면 됩니다.
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from kordoc import KordocClient, KordocConfig
|
|
24
|
+
|
|
25
|
+
config = KordocConfig(
|
|
26
|
+
node="/opt/node/bin/node",
|
|
27
|
+
cli="/opt/kordoc-offline/node_modules/kordoc/dist/cli.js",
|
|
28
|
+
env={"KORDOC_OFFLINE": "1"},
|
|
29
|
+
)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 사용
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from kordoc import KordocClient
|
|
36
|
+
|
|
37
|
+
with KordocClient() as client:
|
|
38
|
+
result = client.parse("문서.hwpx", table_format="gfm") # 중첩 표를 부모·자식 트리로 편 GFM
|
|
39
|
+
if result.success:
|
|
40
|
+
print(result.markdown)
|
|
41
|
+
else:
|
|
42
|
+
print(result.code, result.error) # 문서를 읽지 못한 것은 예외가 아니다
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import asyncio
|
|
47
|
+
from kordoc import AsyncKordocClient
|
|
48
|
+
|
|
49
|
+
async def main() -> None:
|
|
50
|
+
async with AsyncKordocClient(max_workers=2) as client:
|
|
51
|
+
results = await asyncio.gather(*(client.parse(p) for p in ["a.hwp", "b.pdf"]))
|
|
52
|
+
|
|
53
|
+
asyncio.run(main())
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- `parse(path, **options)` · `parse_bytes(data, **options)` · `warmup(path, **options)` · `close()`(`aclose()`)
|
|
57
|
+
- `with` / `async with` 를 나가면 소유 워커를 모두 종료·회수합니다. 패키지 import·클라이언트 생성만으로는 프로세스를 띄우지 않습니다.
|
|
58
|
+
- `result.raw` 는 엔진 결과 JSON 원본입니다(알 수 없는 필드 포함). `markdown`·`blocks`·`pages`·`metadata`·`warnings`·`images` 를 꺼내 쓸 수 있습니다.
|
|
59
|
+
- `result.raise_for_error()` 는 파싱 실패를 `KordocParseFailed` 로 바꿉니다.
|
|
60
|
+
|
|
61
|
+
### 파싱 옵션
|
|
62
|
+
|
|
63
|
+
키워드 인자로 줍니다. 빼면 엔진 기본값이고, `False` 를 주면 실제로 끕니다(`ocr=False` 는 OCR 끔).
|
|
64
|
+
|
|
65
|
+
`table_format`(`"gfm"`) · `html_tables` · `layout_tables`(`"visual"`·`"keep"`) · `classify_tables` · `tables` · `plain` · `script_tags` ·
|
|
66
|
+
`remove_header_footer` · `dedupe_running_headers` · `keep_trailing_empty_cols` · `keep_empty_paragraphs` · `include_field_placeholders` ·
|
|
67
|
+
`pages`(`"1-3"` 또는 `[1, 3]`) · `images` · `inline_images` · `ocr`(`True`·`False`·`"force"`) · `formula_ocr` · `password`
|
|
68
|
+
|
|
69
|
+
함수 값(OCR 프로바이더, 진행 콜백)과 입력 경로를 바꾸는 옵션은 받지 않습니다. 잘못된 옵션은 `KordocProtocolError`(`code="INVALID_OPTIONS"`)입니다.
|
|
70
|
+
|
|
71
|
+
### 이미지
|
|
72
|
+
|
|
73
|
+
| 방식 | 사용 | 동작 |
|
|
74
|
+
| --- | --- | --- |
|
|
75
|
+
| inline (기본) | `parse(p)` | 이미지 바이트를 결과 JSON 에 base64 로 받습니다 |
|
|
76
|
+
| files | `parse(p, image_transport="files", assets_dir="/data/assets")` | `assets_dir` 아래 요청마다 새 디렉터리에 파일로 받습니다 |
|
|
77
|
+
|
|
78
|
+
`result.images[i].read()` 를 부를 때만 바이트를 해독하거나 파일을 읽습니다. `filename` 은 Markdown 이 가리키는 이름, `path` 는 files 방식의 실제 파일입니다.
|
|
79
|
+
files 방식의 파일은 호출자 소유이며 클라이언트를 닫아도 남습니다. 이미지가 많은 문서는 files 방식이 응답을 작게 유지합니다.
|
|
80
|
+
제한 시간·취소로 실행 중인 워커를 종료하면 그 요청이 쓰던 `assets_dir/kordoc-<id>-*` 디렉터리가 남을 수 있습니다 — `assets_dir` 정리는 호출자가 합니다.
|
|
81
|
+
`images=False` 는 이미지 바이트 추출 자체를 생략하는 엔진 옵션으로, 전송 방식과 별개입니다.
|
|
82
|
+
|
|
83
|
+
## 워커 수명
|
|
84
|
+
|
|
85
|
+
| 설정 (`KordocConfig`) | 기본 | 동작 |
|
|
86
|
+
| --- | --- | --- |
|
|
87
|
+
| `max_workers` | 1 | 상주 워커 수. 워커 하나는 한 번에 요청 하나만 처리합니다. 코어 수로 자동으로 늘리지 않습니다 |
|
|
88
|
+
| `max_queue` | 64 | 워커를 기다리는 요청 수 상한. 넘으면 `KordocQueueFull` |
|
|
89
|
+
| `request_timeout` | 없음 | 요청 제한 시간(초). 대기 큐 입장부터 결과 수신까지 |
|
|
90
|
+
| `start_timeout` | 30 | 워커가 ready 를 보낼 때까지 |
|
|
91
|
+
| `warmup_timeout` | 300 | 워밍업 문서 한 건 |
|
|
92
|
+
| `close_timeout` | 10 | 닫을 때 진행 중 요청을 기다리는 시간 |
|
|
93
|
+
| `max_worker_rss_bytes` | 끔 | 응답의 rss 가 넘으면 그 워커를 다음 작업 전에 새 워커로 바꿉니다 |
|
|
94
|
+
|
|
95
|
+
- **제한 시간·취소**: 대기 중인 요청은 큐에서만 빠집니다. 실행 중인 요청은 담당 워커를 즉시 종료·회수합니다(엔진 파싱은 중간에 멈출 수 없어서입니다). 다음 요청은 새 워커가 받습니다. 늦게 온 응답이 다른 요청에 섞이지 않습니다. 비동기에서는 Task 취소가 같은 규칙을 따릅니다.
|
|
96
|
+
- **장애**: 워커가 끝나거나 응답이 깨지면 그 요청은 `KordocWorkerCrashed` 로 끝나고 자동 재시도하지 않습니다. 다음 요청에서 새 워커를 띄웁니다.
|
|
97
|
+
- **RSS 교체**: `max_worker_rss_bytes` 를 켜면 작업이 끝난 뒤 응답의 rss(완료 시점 값, 처리 중 최댓값 아님)를 보고 교체합니다. N 건마다 재시작하지 않습니다. 풀 메모리는 `worker_info()` 의 rss 합으로 봅니다. Node heap 상한은 별도로 `env={"NODE_OPTIONS": "--max-old-space-size=4096"}` 처럼 정합니다.
|
|
98
|
+
- **워밍업**: `warmup(대표 문서, **options)` 은 모든 워커에서 그 문서를 실제로 파싱하고, 교체된 워커도 요청을 받기 전에 같은 문서로 워밍업합니다. 결과(`WarmupReport`)의 경고는 그대로 전달합니다. OCR 을 건너뛴 경고가 있으면 OCR 준비가 된 것이 아닙니다. 워밍업은 처리 경로를 한 번 지나게 할 뿐 최적화 완료를 보장하지 않습니다.
|
|
99
|
+
- **닫기**: 새 요청을 막고, 대기 요청은 `KordocClosed` 로 끝내고, 진행 요청은 `close_timeout` 까지 기다린 뒤 워커를 종료합니다.
|
|
100
|
+
|
|
101
|
+
## 바이트 입력
|
|
102
|
+
|
|
103
|
+
`parse_bytes(data)` 는 바이트를 SDK 소유 임시 디렉터리(`temp_dir`, 기본 시스템 임시 폴더 아래 `kordoc-sdk-*`)에 파일로 쓴 뒤 파싱합니다.
|
|
104
|
+
큰 입력을 NDJSON 에 다시 싣지 않기 위해서이며 임시 파일 I/O 가 생깁니다. 성공·실패·취소와 관계없이 끝나면 지우고, 닫을 때 디렉터리도 지웁니다.
|
|
105
|
+
원본 파일 경로가 필요한 DRM 문서 대체 경로는 바이트 입력에서 파일 입력과 같게 동작하지 않을 수 있습니다.
|
|
106
|
+
|
|
107
|
+
## 그 밖에
|
|
108
|
+
|
|
109
|
+
- 워커 환경은 현재 환경을 물려받고 `env` 를 덧붙입니다(`KORDOC_OFFLINE`, `KORDOC_MODEL_CACHE` 등). SDK 는 요청 본문·암호를 로그에 남기지 않습니다.
|
|
110
|
+
- `KORDOC_ROOT` 는 MCP 서버의 접근 제한입니다. SDK 는 같은 샌드박스를 제공하지 않으며, 넘긴 경로를 그대로 읽습니다.
|
|
111
|
+
- 워커 프로토콜: [docs/parse-worker-protocol.md](../../docs/parse-worker-protocol.md)
|
|
112
|
+
|
|
113
|
+
## 개발
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npm ci && npm run build # 저장소 루트 — 엔진(dist/cli.js)
|
|
117
|
+
cd sdk/python
|
|
118
|
+
python -m venv .venv && . .venv/bin/activate
|
|
119
|
+
python -m pip install -e '.[test]'
|
|
120
|
+
python -m pytest # 실제 엔진 + 합성 문서(tests/fixtures/sdk-docs.ts) + 장애 주입 워커
|
|
121
|
+
python -m build
|
|
122
|
+
```
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=83"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "kordoc"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python SDK for kordoc — parse Korean documents (HWP, HWPX, PDF, DOCX, XLSX …) through a resident kordoc engine worker"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "chrisryugj" }, { name = "Byeory Jeong" }]
|
|
14
|
+
keywords = ["hwp", "hwpx", "korean", "document", "parser", "markdown", "rag"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Topic :: Text Processing :: Markup",
|
|
20
|
+
]
|
|
21
|
+
dependencies = []
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://github.com/chrisryugj/kordoc"
|
|
25
|
+
Source = "https://github.com/chrisryugj/kordoc/tree/main/sdk/python"
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
test = ["pytest>=8", "build>=1.2"]
|
|
29
|
+
|
|
30
|
+
[tool.setuptools.packages.find]
|
|
31
|
+
where = ["src"]
|
|
32
|
+
|
|
33
|
+
[tool.setuptools.package-data]
|
|
34
|
+
kordoc = ["py.typed"]
|
|
35
|
+
|
|
36
|
+
[tool.pytest.ini_options]
|
|
37
|
+
testpaths = ["tests"]
|
kordoc-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""kordoc Python SDK — 상주 kordoc 엔진 워커(parse-worker protocol 2)로 한국 문서를 파싱한다.
|
|
2
|
+
|
|
3
|
+
from kordoc import KordocClient
|
|
4
|
+
|
|
5
|
+
with KordocClient() as client:
|
|
6
|
+
result = client.parse("문서.hwpx", table_format="gfm")
|
|
7
|
+
print(result.markdown)
|
|
8
|
+
|
|
9
|
+
실행 환경에 Node.js 와 kordoc 엔진(npm `kordoc`)이 따로 있어야 한다. 이 패키지는 엔진을 설치하지 않는다.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from ._client import AsyncKordocClient, KordocClient
|
|
13
|
+
from ._config import KordocConfig
|
|
14
|
+
from ._errors import (
|
|
15
|
+
KordocClosed,
|
|
16
|
+
KordocError,
|
|
17
|
+
KordocIncompatibleEngine,
|
|
18
|
+
KordocParseFailed,
|
|
19
|
+
KordocProtocolError,
|
|
20
|
+
KordocQueueFull,
|
|
21
|
+
KordocStartError,
|
|
22
|
+
KordocTimeout,
|
|
23
|
+
KordocWorkerCrashed,
|
|
24
|
+
)
|
|
25
|
+
from ._result import Image, ParseResult, WarmupReport, WorkerWarmup
|
|
26
|
+
|
|
27
|
+
__version__ = "0.1.0"
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"AsyncKordocClient", "KordocClient", "KordocConfig",
|
|
31
|
+
"ParseResult", "Image", "WarmupReport", "WorkerWarmup",
|
|
32
|
+
"KordocError", "KordocStartError", "KordocIncompatibleEngine", "KordocProtocolError", "KordocWorkerCrashed",
|
|
33
|
+
"KordocTimeout", "KordocQueueFull", "KordocClosed", "KordocParseFailed",
|
|
34
|
+
]
|