groundextract 0.1.2__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.
- groundextract-0.1.2/.dockerignore +34 -0
- groundextract-0.1.2/.github/CODEOWNERS +24 -0
- groundextract-0.1.2/.github/ISSUE_TEMPLATE/bug_report.yml +108 -0
- groundextract-0.1.2/.github/ISSUE_TEMPLATE/config.yml +11 -0
- groundextract-0.1.2/.github/ISSUE_TEMPLATE/feature_request.yml +90 -0
- groundextract-0.1.2/.github/ISSUE_TEMPLATE/rule_pack_request.yml +105 -0
- groundextract-0.1.2/.github/PULL_REQUEST_TEMPLATE.md +69 -0
- groundextract-0.1.2/.github/workflows/ci.yml +79 -0
- groundextract-0.1.2/.gitignore +73 -0
- groundextract-0.1.2/CHANGELOG.md +355 -0
- groundextract-0.1.2/CITATION.cff +41 -0
- groundextract-0.1.2/CODE_OF_CONDUCT.md +142 -0
- groundextract-0.1.2/CONTRIBUTING.md +248 -0
- groundextract-0.1.2/Dockerfile +22 -0
- groundextract-0.1.2/LICENSE +201 -0
- groundextract-0.1.2/Makefile +22 -0
- groundextract-0.1.2/PKG-INFO +566 -0
- groundextract-0.1.2/README.ko.md +474 -0
- groundextract-0.1.2/README.md +527 -0
- groundextract-0.1.2/SECURITY.md +108 -0
- groundextract-0.1.2/bench/generate_golden.py +765 -0
- groundextract-0.1.2/bench/golden/balance_clean_01.json +13 -0
- groundextract-0.1.2/bench/golden/balance_misread_noncurrent_01.json +14 -0
- groundextract-0.1.2/bench/golden/gen_bs_001.json +53 -0
- groundextract-0.1.2/bench/golden/gen_bs_002.json +53 -0
- groundextract-0.1.2/bench/golden/gen_bs_003.json +53 -0
- groundextract-0.1.2/bench/golden/gen_bs_004.json +50 -0
- groundextract-0.1.2/bench/golden/gen_bs_005.json +53 -0
- groundextract-0.1.2/bench/golden/gen_bs_006.json +53 -0
- groundextract-0.1.2/bench/golden/gen_bs_007.json +53 -0
- groundextract-0.1.2/bench/golden/gen_bs_008.json +50 -0
- groundextract-0.1.2/bench/golden/gen_bs_009.json +53 -0
- groundextract-0.1.2/bench/golden/gen_bs_010.json +53 -0
- groundextract-0.1.2/bench/golden/gen_ctr_001.json +116 -0
- groundextract-0.1.2/bench/golden/gen_ctr_002.json +116 -0
- groundextract-0.1.2/bench/golden/gen_ctr_003.json +116 -0
- groundextract-0.1.2/bench/golden/gen_ctr_004.json +116 -0
- groundextract-0.1.2/bench/golden/gen_ctr_005.json +116 -0
- groundextract-0.1.2/bench/golden/gen_ctr_006.json +116 -0
- groundextract-0.1.2/bench/golden/gen_is_001.json +81 -0
- groundextract-0.1.2/bench/golden/gen_is_002.json +81 -0
- groundextract-0.1.2/bench/golden/gen_is_003.json +78 -0
- groundextract-0.1.2/bench/golden/gen_is_004.json +81 -0
- groundextract-0.1.2/bench/golden/gen_is_005.json +78 -0
- groundextract-0.1.2/bench/golden/gen_is_006.json +81 -0
- groundextract-0.1.2/bench/golden/gen_is_007.json +78 -0
- groundextract-0.1.2/bench/golden/gen_is_008.json +81 -0
- groundextract-0.1.2/bench/golden/gen_stmt_001.json +22 -0
- groundextract-0.1.2/bench/golden/gen_stmt_002.json +25 -0
- groundextract-0.1.2/bench/golden/gen_stmt_003.json +22 -0
- groundextract-0.1.2/bench/golden/gen_stmt_004.json +25 -0
- groundextract-0.1.2/bench/golden/gen_stmt_005.json +25 -0
- groundextract-0.1.2/bench/golden/gen_stmt_006.json +25 -0
- groundextract-0.1.2/bench/golden/gen_stmt_007.json +25 -0
- groundextract-0.1.2/bench/golden/gen_stmt_008.json +25 -0
- groundextract-0.1.2/bench/golden/gen_stmt_009.json +25 -0
- groundextract-0.1.2/bench/golden/gen_stmt_010.json +25 -0
- groundextract-0.1.2/bench/golden/gen_tax_001.json +43 -0
- groundextract-0.1.2/bench/golden/gen_tax_002.json +29 -0
- groundextract-0.1.2/bench/golden/gen_tax_003.json +32 -0
- groundextract-0.1.2/bench/golden/gen_tax_004.json +43 -0
- groundextract-0.1.2/bench/golden/gen_tax_005.json +32 -0
- groundextract-0.1.2/bench/golden/gen_tax_006.json +32 -0
- groundextract-0.1.2/bench/golden/gen_tax_007.json +46 -0
- groundextract-0.1.2/bench/golden/gen_tax_008.json +32 -0
- groundextract-0.1.2/bench/golden/gen_tax_009.json +32 -0
- groundextract-0.1.2/bench/golden/gen_tax_010.json +43 -0
- groundextract-0.1.2/bench/golden/gen_tax_011.json +32 -0
- groundextract-0.1.2/bench/golden/gen_tax_012.json +29 -0
- groundextract-0.1.2/bench/golden/gen_tax_013.json +46 -0
- groundextract-0.1.2/bench/golden/gen_tax_014.json +32 -0
- groundextract-0.1.2/bench/golden/gen_tax_015.json +32 -0
- groundextract-0.1.2/bench/golden/gen_tax_016.json +43 -0
- groundextract-0.1.2/bench/golden/gen_tax_017.json +29 -0
- groundextract-0.1.2/bench/golden/gen_tax_018.json +32 -0
- groundextract-0.1.2/bench/golden/gen_tax_019.json +46 -0
- groundextract-0.1.2/bench/golden/gen_tax_020.json +32 -0
- groundextract-0.1.2/bench/golden/gen_tax_021.json +32 -0
- groundextract-0.1.2/bench/golden/gen_tax_022.json +46 -0
- groundextract-0.1.2/bench/golden/gen_tax_023.json +32 -0
- groundextract-0.1.2/bench/golden/gen_tax_024.json +32 -0
- groundextract-0.1.2/bench/golden/gen_tax_025.json +46 -0
- groundextract-0.1.2/bench/golden/gen_tax_026.json +29 -0
- groundextract-0.1.2/bench/golden/gen_tax_027.json +29 -0
- groundextract-0.1.2/bench/golden/gen_tax_028.json +46 -0
- groundextract-0.1.2/bench/golden/gen_tax_029.json +32 -0
- groundextract-0.1.2/bench/golden/gen_tax_030.json +32 -0
- groundextract-0.1.2/bench/golden/statement_clean_01.json +9 -0
- groundextract-0.1.2/bench/golden/statement_hallucinated_debit_01.json +9 -0
- groundextract-0.1.2/bench/golden/tax_clean_01.json +10 -0
- groundextract-0.1.2/bench/golden/tax_clean_02.json +10 -0
- groundextract-0.1.2/bench/golden/tax_hallucinated_total_01.json +10 -0
- groundextract-0.1.2/bench/golden/tax_hallucinated_vat_01.json +10 -0
- groundextract-0.1.2/bench/sample_pdf/tax_invoice_sample.html +35 -0
- groundextract-0.1.2/bench/sample_pdf/tax_invoice_sample.pdf +0 -0
- groundextract-0.1.2/bench/verify_llm_only.py +62 -0
- groundextract-0.1.2/bench/verify_real.py +117 -0
- groundextract-0.1.2/groundextract/__init__.py +68 -0
- groundextract-0.1.2/groundextract/__main__.py +262 -0
- groundextract-0.1.2/groundextract/adapters/__init__.py +10 -0
- groundextract-0.1.2/groundextract/adapters/docling_adapter.py +206 -0
- groundextract-0.1.2/groundextract/bench.py +188 -0
- groundextract-0.1.2/groundextract/export.py +126 -0
- groundextract-0.1.2/groundextract/gate.py +383 -0
- groundextract-0.1.2/groundextract/grounding.py +414 -0
- groundextract-0.1.2/groundextract/llm/__init__.py +7 -0
- groundextract-0.1.2/groundextract/llm/base.py +35 -0
- groundextract-0.1.2/groundextract/llm/mock.py +23 -0
- groundextract-0.1.2/groundextract/llm/openweight.py +313 -0
- groundextract-0.1.2/groundextract/mcp_server.py +500 -0
- groundextract-0.1.2/groundextract/models.py +112 -0
- groundextract-0.1.2/groundextract/rules.py +372 -0
- groundextract-0.1.2/pyproject.toml +108 -0
- groundextract-0.1.2/rules/README.md +155 -0
- groundextract-0.1.2/rules/balance_sheet.yaml +27 -0
- groundextract-0.1.2/rules/corporate_tax_return.yaml +47 -0
- groundextract-0.1.2/rules/income_statement.yaml +41 -0
- groundextract-0.1.2/rules/statement.yaml +24 -0
- groundextract-0.1.2/rules/tax_invoice.yaml +31 -0
- groundextract-0.1.2/tests/test_balance_sheet.py +72 -0
- groundextract-0.1.2/tests/test_bench.py +233 -0
- groundextract-0.1.2/tests/test_cli.py +212 -0
- groundextract-0.1.2/tests/test_docling_adapter.py +158 -0
- groundextract-0.1.2/tests/test_export.py +89 -0
- groundextract-0.1.2/tests/test_fault_localization.py +167 -0
- groundextract-0.1.2/tests/test_filed_return_packs.py +205 -0
- groundextract-0.1.2/tests/test_gate.py +299 -0
- groundextract-0.1.2/tests/test_gate_bypass.py +404 -0
- groundextract-0.1.2/tests/test_grounding.py +135 -0
- groundextract-0.1.2/tests/test_mcp_server.py +426 -0
- groundextract-0.1.2/tests/test_openweight.py +294 -0
- groundextract-0.1.2/tests/test_rules.py +162 -0
- groundextract-0.1.2/viewer/README.md +41 -0
- groundextract-0.1.2/viewer/assets/balance_sheet_demo.png +0 -0
- groundextract-0.1.2/viewer/assets/tax_invoice_demo.png +0 -0
- groundextract-0.1.2/viewer/build_combined_fixture.py +277 -0
- groundextract-0.1.2/viewer/fixtures/clean.json +169 -0
- groundextract-0.1.2/viewer/fixtures/fixtures.js +341 -0
- groundextract-0.1.2/viewer/fixtures/image_data.js +352 -0
- groundextract-0.1.2/viewer/fixtures/injected.json +169 -0
- groundextract-0.1.2/viewer/index.html +353 -0
- groundextract-0.1.2/viewer/samples/balance_sheet_sample.pdf +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# The Dockerfile uses an allowlist of COPY paths, so this file is only a second
|
|
2
|
+
# line of defence (and keeps the build context small). Anything private listed
|
|
3
|
+
# here is also absent from the image because it was never named for copying.
|
|
4
|
+
.git
|
|
5
|
+
.github
|
|
6
|
+
.venv
|
|
7
|
+
venv
|
|
8
|
+
env
|
|
9
|
+
**/__pycache__
|
|
10
|
+
*.py[cod]
|
|
11
|
+
*.egg-info
|
|
12
|
+
build
|
|
13
|
+
dist
|
|
14
|
+
.pytest_cache
|
|
15
|
+
.mypy_cache
|
|
16
|
+
.ruff_cache
|
|
17
|
+
.coverage
|
|
18
|
+
coverage.xml
|
|
19
|
+
htmlcov
|
|
20
|
+
sbom
|
|
21
|
+
# Private / unmasked sources — mirrors .gitignore. Never let these into a layer.
|
|
22
|
+
local
|
|
23
|
+
submission
|
|
24
|
+
docs
|
|
25
|
+
CLAUDE.md
|
|
26
|
+
bench/sample_pdf/real_*
|
|
27
|
+
.env
|
|
28
|
+
.env.*
|
|
29
|
+
*.local
|
|
30
|
+
*.local.*
|
|
31
|
+
.idea
|
|
32
|
+
.vscode
|
|
33
|
+
.DS_Store
|
|
34
|
+
Thumbs.db
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Code owners for GroundExtract-KR.
|
|
2
|
+
# Listed owners are requested for review automatically on matching pull requests.
|
|
3
|
+
# Syntax: https://docs.github.com/articles/about-code-owners
|
|
4
|
+
|
|
5
|
+
# Default owner for everything in the repository.
|
|
6
|
+
* @sos37591-prog
|
|
7
|
+
|
|
8
|
+
# Trust boundary: the deterministic gate. Changes here can turn a discarded
|
|
9
|
+
# hallucination into a verified one, so they always need an owner review.
|
|
10
|
+
/groundextract/grounding.py @sos37591-prog
|
|
11
|
+
/groundextract/rules.py @sos37591-prog
|
|
12
|
+
/groundextract/gate.py @sos37591-prog
|
|
13
|
+
/groundextract/models.py @sos37591-prog
|
|
14
|
+
|
|
15
|
+
# Domain invariants and the labeled benchmark that measures them.
|
|
16
|
+
/rules/ @sos37591-prog
|
|
17
|
+
/bench/ @sos37591-prog
|
|
18
|
+
/groundextract/bench.py @sos37591-prog
|
|
19
|
+
|
|
20
|
+
# Public interfaces and supply chain.
|
|
21
|
+
/groundextract/mcp_server.py @sos37591-prog
|
|
22
|
+
/pyproject.toml @sos37591-prog
|
|
23
|
+
/.github/ @sos37591-prog
|
|
24
|
+
/SECURITY.md @sos37591-prog
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: A wrong verdict, a crash, or anything that does not behave as documented
|
|
3
|
+
title: "bug: "
|
|
4
|
+
labels: ["bug"]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: |
|
|
9
|
+
Thanks for the report.
|
|
10
|
+
|
|
11
|
+
**Before you file:** if a *wrong number came out `verified`* (a gate bypass /
|
|
12
|
+
false negative), please do not open a public issue — report it privately via
|
|
13
|
+
[Security Advisory](https://github.com/sos37591-prog/groundextract-kr/security/advisories/new)
|
|
14
|
+
so the fix can ship before the technique is public. See [SECURITY.md](https://github.com/sos37591-prog/groundextract-kr/blob/main/SECURITY.md).
|
|
15
|
+
|
|
16
|
+
**Never paste real documents.** No real tax invoices, financial statements,
|
|
17
|
+
registration numbers, or personal data — reproduce with synthetic text.
|
|
18
|
+
|
|
19
|
+
- type: checkboxes
|
|
20
|
+
id: preflight
|
|
21
|
+
attributes:
|
|
22
|
+
label: Preflight
|
|
23
|
+
options:
|
|
24
|
+
- label: This is not a gate bypass (a hallucinated value coming out `verified`) — those go to Security Advisories
|
|
25
|
+
required: true
|
|
26
|
+
- label: My reproduction uses synthetic data only, with no real personal or corporate information
|
|
27
|
+
required: true
|
|
28
|
+
- label: I searched existing issues and did not find a duplicate
|
|
29
|
+
required: true
|
|
30
|
+
|
|
31
|
+
- type: dropdown
|
|
32
|
+
id: area
|
|
33
|
+
attributes:
|
|
34
|
+
label: Area
|
|
35
|
+
options:
|
|
36
|
+
- grounding (verbatim matching)
|
|
37
|
+
- rules (arithmetic rule engine / rule packs)
|
|
38
|
+
- gate (verdicts, confidence, sibling discard)
|
|
39
|
+
- bench (NumHall-KR, golden set generator)
|
|
40
|
+
- mcp_server
|
|
41
|
+
- llm / adapters (Ollama, Docling)
|
|
42
|
+
- viewer
|
|
43
|
+
- packaging / CI / docs
|
|
44
|
+
- not sure
|
|
45
|
+
validations:
|
|
46
|
+
required: true
|
|
47
|
+
|
|
48
|
+
- type: textarea
|
|
49
|
+
id: what-happened
|
|
50
|
+
attributes:
|
|
51
|
+
label: What happened
|
|
52
|
+
description: What you observed, and what you expected instead.
|
|
53
|
+
placeholder: |
|
|
54
|
+
`total` came out verified=... but I expected ...
|
|
55
|
+
validations:
|
|
56
|
+
required: true
|
|
57
|
+
|
|
58
|
+
- type: textarea
|
|
59
|
+
id: repro
|
|
60
|
+
attributes:
|
|
61
|
+
label: Minimal reproduction
|
|
62
|
+
description: |
|
|
63
|
+
The smallest self-contained snippet that shows the problem — document text,
|
|
64
|
+
extracted values, and `doc_type`. Synthetic data only.
|
|
65
|
+
render: python
|
|
66
|
+
value: |
|
|
67
|
+
from groundextract import ExtractedValue, load_rule_pack, run_gate
|
|
68
|
+
|
|
69
|
+
doc = "공급가액 1,000,000원\n세액 100,000원\n합계금액 1,100,000원"
|
|
70
|
+
pack = load_rule_pack("rules/tax_invoice.yaml")
|
|
71
|
+
values = [
|
|
72
|
+
ExtractedValue("supply", "1,000,000원", 1_000_000, "공급가액 1,000,000원"),
|
|
73
|
+
]
|
|
74
|
+
for f in run_gate(values, doc, pack):
|
|
75
|
+
print(f.field, f.verdict.value, f.confidence, [c.detail for c in f.failed_checks])
|
|
76
|
+
|
|
77
|
+
- type: textarea
|
|
78
|
+
id: output
|
|
79
|
+
attributes:
|
|
80
|
+
label: Actual output
|
|
81
|
+
description: Full traceback, or the verdicts and failed checks you got.
|
|
82
|
+
render: text
|
|
83
|
+
validations:
|
|
84
|
+
required: true
|
|
85
|
+
|
|
86
|
+
- type: input
|
|
87
|
+
id: version
|
|
88
|
+
attributes:
|
|
89
|
+
label: Version / commit
|
|
90
|
+
description: "`python -c \"import groundextract; print(groundextract.__version__)\"`, or the commit SHA."
|
|
91
|
+
placeholder: "0.1.0 (or commit 5aeff8b)"
|
|
92
|
+
validations:
|
|
93
|
+
required: true
|
|
94
|
+
|
|
95
|
+
- type: input
|
|
96
|
+
id: env
|
|
97
|
+
attributes:
|
|
98
|
+
label: Python and OS
|
|
99
|
+
description: "`python -V` and your operating system."
|
|
100
|
+
placeholder: "Python 3.11.9 / Ubuntu 24.04"
|
|
101
|
+
validations:
|
|
102
|
+
required: true
|
|
103
|
+
|
|
104
|
+
- type: textarea
|
|
105
|
+
id: extra
|
|
106
|
+
attributes:
|
|
107
|
+
label: Anything else
|
|
108
|
+
description: Optional context — related issues, why the input is realistic, a suggested fix.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
blank_issues_enabled: false
|
|
2
|
+
contact_links:
|
|
3
|
+
- name: Report a gate bypass (security)
|
|
4
|
+
url: https://github.com/sos37591-prog/groundextract-kr/security/advisories/new
|
|
5
|
+
about: A wrong number that comes out `verified` is a false negative and the most serious bug class here. Report it privately, not as a public issue.
|
|
6
|
+
- name: Contributing guide and good first issues
|
|
7
|
+
url: https://github.com/sos37591-prog/groundextract-kr/blob/main/CONTRIBUTING.md
|
|
8
|
+
about: Dev setup, how to add a rule pack for a new document type, PR rules, and a list of good first issues.
|
|
9
|
+
- name: How the gate works (README)
|
|
10
|
+
url: https://github.com/sos37591-prog/groundextract-kr/blob/main/README.md
|
|
11
|
+
about: Quick start, the two disjoint checks, MCP usage, honest benchmark numbers, and known limitations.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Propose a change to how the gate works, or a new capability
|
|
3
|
+
title: "feat: "
|
|
4
|
+
labels: ["enhancement"]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: |
|
|
9
|
+
Requesting support for a **new document type**? Use the
|
|
10
|
+
[Rule pack request](https://github.com/sos37591-prog/groundextract-kr/issues/new?template=rule_pack_request.yml)
|
|
11
|
+
template instead — it asks the right questions.
|
|
12
|
+
|
|
13
|
+
Two things shape what gets merged here:
|
|
14
|
+
|
|
15
|
+
- **Recall is the product.** A change that lets a wrong number through to
|
|
16
|
+
`verified` will not be accepted, however much precision it buys.
|
|
17
|
+
- **One runtime dependency.** PyYAML is the whole runtime dependency list.
|
|
18
|
+
Anything heavier has to live in an optional extra and be imported lazily.
|
|
19
|
+
|
|
20
|
+
- type: textarea
|
|
21
|
+
id: problem
|
|
22
|
+
attributes:
|
|
23
|
+
label: The problem
|
|
24
|
+
description: What goes wrong today? Concrete situations beat abstract wishes.
|
|
25
|
+
placeholder: |
|
|
26
|
+
When a statement is printed in 천원 units, every arithmetic rule fires a false
|
|
27
|
+
violation, so a clean document comes out fully discarded.
|
|
28
|
+
validations:
|
|
29
|
+
required: true
|
|
30
|
+
|
|
31
|
+
- type: textarea
|
|
32
|
+
id: proposal
|
|
33
|
+
attributes:
|
|
34
|
+
label: Proposed solution
|
|
35
|
+
description: What should happen instead? Sketch the API or behavior if you can.
|
|
36
|
+
validations:
|
|
37
|
+
required: true
|
|
38
|
+
|
|
39
|
+
- type: dropdown
|
|
40
|
+
id: area
|
|
41
|
+
attributes:
|
|
42
|
+
label: Area
|
|
43
|
+
options:
|
|
44
|
+
- grounding (verbatim matching)
|
|
45
|
+
- rules (rule engine expressiveness)
|
|
46
|
+
- gate (verdicts, confidence, fault localization)
|
|
47
|
+
- bench (metrics, golden set)
|
|
48
|
+
- mcp_server (tools, schema)
|
|
49
|
+
- llm / adapters
|
|
50
|
+
- viewer
|
|
51
|
+
- packaging / CI / docs
|
|
52
|
+
validations:
|
|
53
|
+
required: true
|
|
54
|
+
|
|
55
|
+
- type: dropdown
|
|
56
|
+
id: impact
|
|
57
|
+
attributes:
|
|
58
|
+
label: Effect on the benchmark
|
|
59
|
+
description: Your best guess. We report precision and recall movements in the open.
|
|
60
|
+
options:
|
|
61
|
+
- No effect on verdicts (tooling, docs, performance)
|
|
62
|
+
- Improves precision, recall unchanged at 100%
|
|
63
|
+
- Improves recall coverage (catches a class of error we currently miss)
|
|
64
|
+
- Trades recall for something else (needs strong justification)
|
|
65
|
+
- Not sure
|
|
66
|
+
validations:
|
|
67
|
+
required: true
|
|
68
|
+
|
|
69
|
+
- type: textarea
|
|
70
|
+
id: alternatives
|
|
71
|
+
attributes:
|
|
72
|
+
label: Alternatives considered
|
|
73
|
+
description: Including "why not just do it outside the gate?"
|
|
74
|
+
|
|
75
|
+
- type: input
|
|
76
|
+
id: deps
|
|
77
|
+
attributes:
|
|
78
|
+
label: New dependencies required
|
|
79
|
+
description: Name them, or write "none". Runtime dependencies beyond PyYAML are very unlikely to be accepted.
|
|
80
|
+
placeholder: "none"
|
|
81
|
+
validations:
|
|
82
|
+
required: true
|
|
83
|
+
|
|
84
|
+
- type: checkboxes
|
|
85
|
+
id: help
|
|
86
|
+
attributes:
|
|
87
|
+
label: Participation
|
|
88
|
+
options:
|
|
89
|
+
- label: I am willing to open a pull request for this
|
|
90
|
+
- label: I would like to discuss the design first
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
name: Rule pack request (new document type)
|
|
2
|
+
description: Ask for — or propose — arithmetic invariants for a document type we don't support yet
|
|
3
|
+
title: "rules: <document type>"
|
|
4
|
+
labels: ["rule-pack", "enhancement"]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: |
|
|
9
|
+
A rule pack is how this project grows. It is one YAML file listing the
|
|
10
|
+
arithmetic identities a document type must satisfy, plus three registrations
|
|
11
|
+
(rule pack → extractor field specs → MCP doc-type enum). See
|
|
12
|
+
[CONTRIBUTING.md](https://github.com/sos37591-prog/groundextract-kr/blob/main/CONTRIBUTING.md#adding-a-rule-pack-new-document-type).
|
|
13
|
+
|
|
14
|
+
You do **not** need to know Python to file this. Domain knowledge — *which
|
|
15
|
+
numbers on this form must add up to which other numbers* — is the part we
|
|
16
|
+
cannot look up, and it is what makes a rule pack worth having.
|
|
17
|
+
|
|
18
|
+
Currently supported: `tax_invoice`, `statement` (trial balance),
|
|
19
|
+
`balance_sheet`.
|
|
20
|
+
|
|
21
|
+
**Do not attach a real document.** Retype the layout with made-up numbers.
|
|
22
|
+
|
|
23
|
+
- type: input
|
|
24
|
+
id: doc-type
|
|
25
|
+
attributes:
|
|
26
|
+
label: Document type
|
|
27
|
+
description: Korean name and the proposed `doc_type` identifier (snake_case, ASCII).
|
|
28
|
+
placeholder: "급여명세서 (payslip) → payslip"
|
|
29
|
+
validations:
|
|
30
|
+
required: true
|
|
31
|
+
|
|
32
|
+
- type: textarea
|
|
33
|
+
id: why
|
|
34
|
+
attributes:
|
|
35
|
+
label: Why this document type
|
|
36
|
+
description: |
|
|
37
|
+
Who processes it, at what volume, and what goes wrong when a number is wrong?
|
|
38
|
+
A wrong number that someone eventually pays for is the strongest case.
|
|
39
|
+
placeholder: |
|
|
40
|
+
Payslips are re-keyed into payroll every month; a misread deduction is only
|
|
41
|
+
caught at year-end settlement, if at all.
|
|
42
|
+
validations:
|
|
43
|
+
required: true
|
|
44
|
+
|
|
45
|
+
- type: textarea
|
|
46
|
+
id: fields
|
|
47
|
+
attributes:
|
|
48
|
+
label: Fields to extract
|
|
49
|
+
description: >-
|
|
50
|
+
One per line as `field_name: 한국어 라벨 (English gloss)`. Field names are
|
|
51
|
+
ASCII snake_case and are what the rules reference.
|
|
52
|
+
render: text
|
|
53
|
+
value: |
|
|
54
|
+
gross_pay: 지급총액 (gross pay)
|
|
55
|
+
total_deductions: 공제총액 (total deductions)
|
|
56
|
+
net_pay: 실지급액 (net pay)
|
|
57
|
+
|
|
58
|
+
- type: textarea
|
|
59
|
+
id: invariants
|
|
60
|
+
attributes:
|
|
61
|
+
label: Arithmetic invariants
|
|
62
|
+
description: |
|
|
63
|
+
The identities that must hold. Plain arithmetic is fine — someone can translate
|
|
64
|
+
it into YAML. Note any legal rounding tolerance (`tol`), since Korean forms
|
|
65
|
+
often round to the won.
|
|
66
|
+
render: text
|
|
67
|
+
value: |
|
|
68
|
+
net_pay = gross_pay - total_deductions (tol 1 KRW)
|
|
69
|
+
gross_pay = sum of allowance line items (tol 1 KRW)
|
|
70
|
+
|
|
71
|
+
- type: textarea
|
|
72
|
+
id: sample
|
|
73
|
+
attributes:
|
|
74
|
+
label: Synthetic sample document
|
|
75
|
+
description: |
|
|
76
|
+
Retype the layout as plain text with **made-up** numbers that satisfy the
|
|
77
|
+
invariants above. This becomes the test fixture — it is the single most useful
|
|
78
|
+
thing you can attach.
|
|
79
|
+
render: text
|
|
80
|
+
placeholder: |
|
|
81
|
+
급여명세서
|
|
82
|
+
지급총액 3,500,000원
|
|
83
|
+
공제총액 420,000원
|
|
84
|
+
실지급액 3,080,000원
|
|
85
|
+
validations:
|
|
86
|
+
required: true
|
|
87
|
+
|
|
88
|
+
- type: textarea
|
|
89
|
+
id: pitfalls
|
|
90
|
+
attributes:
|
|
91
|
+
label: Formatting pitfalls
|
|
92
|
+
description: |
|
|
93
|
+
Negative sign conventions (△, parentheses), unit headers (단위: 천원), full-width
|
|
94
|
+
digits, values split across columns, subtotals that legitimately do not add up.
|
|
95
|
+
placeholder: "Amounts are printed in 천원 units; deductions use △ for negatives."
|
|
96
|
+
|
|
97
|
+
- type: checkboxes
|
|
98
|
+
id: participation
|
|
99
|
+
attributes:
|
|
100
|
+
label: Participation
|
|
101
|
+
options:
|
|
102
|
+
- label: I can write the YAML rule pack and the test myself
|
|
103
|
+
- label: I can review a rule pack someone else writes, as a domain expert
|
|
104
|
+
- label: I confirm the sample above is synthetic and contains no real personal or corporate data
|
|
105
|
+
required: true
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Thanks for contributing to GroundExtract-KR.
|
|
3
|
+
One concern per PR. Delete sections that genuinely do not apply.
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
## What and why
|
|
7
|
+
|
|
8
|
+
<!-- What does this change, and what problem does it solve? Link the issue: Fixes #123 -->
|
|
9
|
+
|
|
10
|
+
## Type of change
|
|
11
|
+
|
|
12
|
+
- [ ] Bug fix (a wrong verdict, a crash, or documented behavior that wasn't true)
|
|
13
|
+
- [ ] New rule pack / document type
|
|
14
|
+
- [ ] New feature or behavior change
|
|
15
|
+
- [ ] Docs only
|
|
16
|
+
- [ ] Build, CI, or packaging
|
|
17
|
+
|
|
18
|
+
## Verification
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
python -m pytest -q →
|
|
22
|
+
python -m ruff check . →
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
<!-- Paste the actual result lines. Both must be green. -->
|
|
26
|
+
|
|
27
|
+
- [ ] `python -m pytest -q` passes
|
|
28
|
+
- [ ] `python -m ruff check .` passes
|
|
29
|
+
- [ ] New behavior has a test; a bug fix has a regression test that fails without the fix
|
|
30
|
+
|
|
31
|
+
## Benchmark impact
|
|
32
|
+
|
|
33
|
+
Run `python -m groundextract.bench` if this touches `grounding.py`, `rules.py`,
|
|
34
|
+
`gate.py`, the rule packs, or the golden set. Paste before/after — we track these
|
|
35
|
+
numbers in the open, including when they get worse.
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
before: NHR 19.9% -> 0.0% | recall 100.0% | precision 35.8% | grounded-accuracy 64.4%
|
|
39
|
+
after:
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- [ ] No benchmark change (this PR cannot affect verdicts)
|
|
43
|
+
- [ ] Numbers changed and are pasted above, with `CHANGELOG.md` updated
|
|
44
|
+
- [ ] **`Auto-Discard Recall` is still 100.0%** — if not, explain below why letting a
|
|
45
|
+
labeled bad value through is acceptable
|
|
46
|
+
|
|
47
|
+
## Rule pack PRs only
|
|
48
|
+
|
|
49
|
+
- [ ] `rules/<doc_type>.yaml` added (`doc_type` matches the filename stem)
|
|
50
|
+
- [ ] `FIELD_SPECS["<doc_type>"]` added in `groundextract/llm/openweight.py`
|
|
51
|
+
- [ ] `<doc_type>` added to `_DOC_TYPE_SCHEMA["enum"]` in `groundextract/mcp_server.py`
|
|
52
|
+
- [ ] Test covering one clean document (all `verified`) and one violating document
|
|
53
|
+
(`discarded`)
|
|
54
|
+
|
|
55
|
+
## Project invariants
|
|
56
|
+
|
|
57
|
+
- [ ] No new **runtime** dependency (PyYAML is the whole list; heavier things go in an
|
|
58
|
+
optional extra and are imported lazily)
|
|
59
|
+
- [ ] The gate path stays deterministic and offline — no randomness, no wall-clock
|
|
60
|
+
dependence, no network in `grounding.py` / `rules.py` / `gate.py`
|
|
61
|
+
- [ ] No real documents, personal data, registration numbers, or company-identifying
|
|
62
|
+
information anywhere in the diff — fixtures are synthetic
|
|
63
|
+
- [ ] `CHANGELOG.md` updated under `## [Unreleased]` for user-visible changes
|
|
64
|
+
- [ ] Docs updated if behavior or commands changed (`README.md` **and**
|
|
65
|
+
`README.ko.md` stay in sync)
|
|
66
|
+
|
|
67
|
+
## Notes for reviewers
|
|
68
|
+
|
|
69
|
+
<!-- Trade-offs you made, things you are unsure about, what to look at first. -->
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
# A moved tag would silently change what runs here, so third-party actions are
|
|
9
|
+
# pinned to commit SHAs (the trailing comment records the human-readable tag).
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
test:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
timeout-minutes: 15
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Python 3.11
|
|
21
|
+
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.11"
|
|
24
|
+
|
|
25
|
+
- name: Install package + dev deps
|
|
26
|
+
run: |
|
|
27
|
+
python -m pip install --upgrade pip
|
|
28
|
+
python -m pip install -e ".[dev]"
|
|
29
|
+
|
|
30
|
+
- name: Lint (ruff)
|
|
31
|
+
run: ruff check .
|
|
32
|
+
|
|
33
|
+
- name: Test (pytest + coverage)
|
|
34
|
+
run: pytest -q --cov=groundextract --cov-report=term-missing
|
|
35
|
+
|
|
36
|
+
sbom:
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
timeout-minutes: 15
|
|
39
|
+
needs: test
|
|
40
|
+
steps:
|
|
41
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
42
|
+
|
|
43
|
+
- name: Set up Python 3.11
|
|
44
|
+
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
|
45
|
+
with:
|
|
46
|
+
python-version: "3.11"
|
|
47
|
+
|
|
48
|
+
- name: Install package + SBOM tooling
|
|
49
|
+
run: |
|
|
50
|
+
python -m pip install --upgrade pip
|
|
51
|
+
# Install the extras a user can actually pull in, not just the runtime
|
|
52
|
+
# dep — the licence question lives in the optional surface. `docling`
|
|
53
|
+
# is left out deliberately: it drags in a multi-GB ML stack that would
|
|
54
|
+
# dominate this job, and it is scanned separately when it changes.
|
|
55
|
+
python -m pip install -e ".[fixture]"
|
|
56
|
+
python -m pip install pip-licenses cyclonedx-bom
|
|
57
|
+
|
|
58
|
+
- name: License allowlist (deny GPL/AGPL copyleft)
|
|
59
|
+
run: |
|
|
60
|
+
# Fail the build if a strong-copyleft license sneaks into deps.
|
|
61
|
+
pip-licenses --format=markdown --output-file=licenses.md
|
|
62
|
+
cat licenses.md
|
|
63
|
+
if pip-licenses --format=csv | grep -Ei 'GPL|AGPL' | grep -vi 'LGPL'; then
|
|
64
|
+
echo "::error::Disallowed copyleft license found in dependencies"; exit 1
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
- name: Generate CycloneDX SBOM
|
|
68
|
+
run: |
|
|
69
|
+
mkdir -p sbom
|
|
70
|
+
cyclonedx-py environment -o sbom/bom.cdx.json
|
|
71
|
+
echo "SBOM generated:"; head -c 400 sbom/bom.cdx.json; echo
|
|
72
|
+
|
|
73
|
+
- name: Upload SBOM artifact
|
|
74
|
+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
75
|
+
with:
|
|
76
|
+
name: sbom
|
|
77
|
+
path: |
|
|
78
|
+
sbom/bom.cdx.json
|
|
79
|
+
licenses.md
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
env/
|
|
11
|
+
|
|
12
|
+
# Test / coverage
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.coverage
|
|
15
|
+
coverage.xml
|
|
16
|
+
htmlcov/
|
|
17
|
+
.mypy_cache/
|
|
18
|
+
.ruff_cache/
|
|
19
|
+
|
|
20
|
+
# SBOM (CI-generated; regenerated each run)
|
|
21
|
+
sbom/*.json
|
|
22
|
+
|
|
23
|
+
# Notebooks / JS tooling (viewer is build-step-free today; keep it that way)
|
|
24
|
+
.ipynb_checkpoints/
|
|
25
|
+
node_modules/
|
|
26
|
+
.tox/
|
|
27
|
+
.nox/
|
|
28
|
+
|
|
29
|
+
# Editors / OS
|
|
30
|
+
.idea/
|
|
31
|
+
.vscode/
|
|
32
|
+
.DS_Store
|
|
33
|
+
Thumbs.db
|
|
34
|
+
desktop.ini
|
|
35
|
+
|
|
36
|
+
# Secrets / local env.
|
|
37
|
+
# Patterns, not filenames: every rule below exists because *this* repository
|
|
38
|
+
# handles real 세무·재무 documents, and a denylist of known names only protects
|
|
39
|
+
# against the mistakes already made. `.env` alone missed `.env.production`;
|
|
40
|
+
# `*.local` alone missed `config.local.json`.
|
|
41
|
+
.env
|
|
42
|
+
.env.*
|
|
43
|
+
.envrc
|
|
44
|
+
*.local
|
|
45
|
+
*.local.*
|
|
46
|
+
*.key
|
|
47
|
+
*.pem
|
|
48
|
+
*.pfx
|
|
49
|
+
*.p12
|
|
50
|
+
*token*
|
|
51
|
+
credentials.json
|
|
52
|
+
service-account.json
|
|
53
|
+
|
|
54
|
+
# Runtime artifacts
|
|
55
|
+
*.log
|
|
56
|
+
*.sqlite
|
|
57
|
+
*.db
|
|
58
|
+
|
|
59
|
+
# Contest submission artifacts (개인정보/PII 포함 — keep out of public repo)
|
|
60
|
+
submission/
|
|
61
|
+
|
|
62
|
+
# Internal planning notes (직원 실명·IP·리소스 리스크 — 로컬 보관, 공개 저장소 제외)
|
|
63
|
+
docs/
|
|
64
|
+
|
|
65
|
+
# Local agent notes: contest schedule, submission paths, unreleased risks.
|
|
66
|
+
# Do not rely on a global gitignore for this — it must be excluded on any machine.
|
|
67
|
+
CLAUDE.md
|
|
68
|
+
|
|
69
|
+
# Unmasked real sources (PII / 실제 기업 재무·세무 원본) — never commit.
|
|
70
|
+
# Prefixed by convention rather than named one by one: the next real document
|
|
71
|
+
# someone drops in must be excluded before it exists, not after.
|
|
72
|
+
bench/sample_pdf/real_*
|
|
73
|
+
local/
|