structverify 0.3.0__py3-none-any.whl

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.
Files changed (168) hide show
  1. structverify/__init__.py +83 -0
  2. structverify/adaptation/__init__.py +0 -0
  3. structverify/adaptation/adapter_trainer.py +341 -0
  4. structverify/adaptation/feedback_store.py +31 -0
  5. structverify/adaptation/kosis_crawler.py +317 -0
  6. structverify/adaptation/sample_builder.py +149 -0
  7. structverify/adaptation/synthetic_generator.py +320 -0
  8. structverify/adaptation/update_embeddings.py +178 -0
  9. structverify/agent/__init__.py +21 -0
  10. structverify/agent/builder_agent.py +226 -0
  11. structverify/agent/conformance_agent.py +171 -0
  12. structverify/agent/dependency_planner.py +151 -0
  13. structverify/agent/indexing_agent.py +153 -0
  14. structverify/agent/indexing_planner.py +169 -0
  15. structverify/agent/integration_example.py +182 -0
  16. structverify/agent/loop.py +1165 -0
  17. structverify/agent/memory.py +207 -0
  18. structverify/agent/planner.py +817 -0
  19. structverify/agent/prompts/__init__.py +15 -0
  20. structverify/agent/prompts/planner_prompts.py +219 -0
  21. structverify/agent/prompts/reflect_prompts.py +387 -0
  22. structverify/agent/reflect.py +227 -0
  23. structverify/agent/runtime_agent.py +1272 -0
  24. structverify/agent/schemas.py +262 -0
  25. structverify/agent/source_profiler.py +229 -0
  26. structverify/agent/tools/__init__.py +64 -0
  27. structverify/agent/tools/base.py +222 -0
  28. structverify/agent/tools/calculate.py +244 -0
  29. structverify/agent/tools/catalog_search.py +859 -0
  30. structverify/agent/tools/deep_explore.py +293 -0
  31. structverify/agent/tools/explore_catalog.py +423 -0
  32. structverify/agent/tools/fetch_evidence.py +922 -0
  33. structverify/agent/tools/finish.py +423 -0
  34. structverify/agent/tools/meta_explore.py +267 -0
  35. structverify/agent/tools/query_rewriter.py +134 -0
  36. structverify/agent/tools/read_original.py +144 -0
  37. structverify/agent/tools/replan.py +365 -0
  38. structverify/agent/workspace.py +958 -0
  39. structverify/api.py +804 -0
  40. structverify/config/default.yaml +350 -0
  41. structverify/core/__init__.py +0 -0
  42. structverify/core/config_loader.py +30 -0
  43. structverify/core/pipeline.py +280 -0
  44. structverify/core/schemas.py +362 -0
  45. structverify/detection/__init__.py +26 -0
  46. structverify/detection/_config.py +163 -0
  47. structverify/detection/_llm.py +24 -0
  48. structverify/detection/candidate/__init__.py +1 -0
  49. structverify/detection/candidate/heuristic.py +60 -0
  50. structverify/detection/candidate/llm.py +51 -0
  51. structverify/detection/candidate_scorer.py +81 -0
  52. structverify/detection/claim_detector.py +164 -0
  53. structverify/detection/claims/__init__.py +1 -0
  54. structverify/detection/claims/worthiness.py +142 -0
  55. structverify/detection/domain/__init__.py +1 -0
  56. structverify/detection/domain/classify.py +84 -0
  57. structverify/detection/domain/preview.py +36 -0
  58. structverify/detection/domain/registry.py +99 -0
  59. structverify/detection/domain_classifier.py +75 -0
  60. structverify/detection/prompts/__init__.py +1 -0
  61. structverify/detection/prompts/candidate.py +38 -0
  62. structverify/detection/prompts/claim_worthiness.py +48 -0
  63. structverify/detection/prompts/domain.py +41 -0
  64. structverify/detection/prompts/schema.py +508 -0
  65. structverify/detection/prompts_loader.py +167 -0
  66. structverify/detection/schema/__init__.py +1 -0
  67. structverify/detection/schema/expand.py +83 -0
  68. structverify/detection/schema/induce.py +441 -0
  69. structverify/detection/schema/regenerate.py +162 -0
  70. structverify/detection/schema/temporal_hints.py +130 -0
  71. structverify/detection/schema/validate.py +193 -0
  72. structverify/detection/schema_inductor.py +112 -0
  73. structverify/detection/synthetic_generator.py +270 -0
  74. structverify/explanation/__init__.py +0 -0
  75. structverify/explanation/_config.py +18 -0
  76. structverify/explanation/_llm.py +25 -0
  77. structverify/explanation/explainer.py +183 -0
  78. structverify/explanation/fallback.py +29 -0
  79. structverify/explanation/formatters.py +75 -0
  80. structverify/explanation/prompts/__init__.py +1 -0
  81. structverify/explanation/prompts/match.py +27 -0
  82. structverify/explanation/prompts/mismatch.py +20 -0
  83. structverify/explanation/prompts/multihop.py +16 -0
  84. structverify/explanation/prompts/unverifiable.py +17 -0
  85. structverify/graph/__init__.py +0 -0
  86. structverify/graph/claim_graph.py +226 -0
  87. structverify/graph/document_graph.py +487 -0
  88. structverify/graph/graph_builder.py +238 -0
  89. structverify/graph/graph_multihop.py +335 -0
  90. structverify/graph/graph_store.py +281 -0
  91. structverify/graph/provenance.py +52 -0
  92. structverify/memory/__init__.py +44 -0
  93. structverify/memory/agent_memory.py +142 -0
  94. structverify/memory/embedder.py +69 -0
  95. structverify/memory/exemplar_store.py +241 -0
  96. structverify/memory/normalizer.py +91 -0
  97. structverify/memory/schema.py +119 -0
  98. structverify/memory/storage/__init__.py +29 -0
  99. structverify/memory/storage/jsonl_store.py +117 -0
  100. structverify/memory/working_memory.py +370 -0
  101. structverify/preprocessing/Dockerfile.scraper +27 -0
  102. structverify/preprocessing/__init__.py +0 -0
  103. structverify/preprocessing/extractor.py +574 -0
  104. structverify/preprocessing/pdf/__init__.py +16 -0
  105. structverify/preprocessing/pdf/fields.py +95 -0
  106. structverify/preprocessing/pdf/markdown.py +107 -0
  107. structverify/preprocessing/pdf/models.py +34 -0
  108. structverify/preprocessing/pdf/ocr.py +172 -0
  109. structverify/preprocessing/pdf/pipeline.py +74 -0
  110. structverify/preprocessing/pdf/reader.py +119 -0
  111. structverify/preprocessing/pdf/scoring.py +61 -0
  112. structverify/preprocessing/scraper_sandbox.py +561 -0
  113. structverify/preprocessing/segmenter.py +48 -0
  114. structverify/preprocessing/sir_builder.py +240 -0
  115. structverify/progress.py +591 -0
  116. structverify/retrieval/__init__.py +0 -0
  117. structverify/retrieval/base.py +208 -0
  118. structverify/retrieval/base_connector.py +85 -0
  119. structverify/retrieval/catalog_ranker.py +300 -0
  120. structverify/retrieval/catalog_search.py +583 -0
  121. structverify/retrieval/chunking.py +92 -0
  122. structverify/retrieval/custom_csv_source.py +386 -0
  123. structverify/retrieval/custom_db_source.py +396 -0
  124. structverify/retrieval/custom_docs_source.py +152 -0
  125. structverify/retrieval/dimension_resolver.py +281 -0
  126. structverify/retrieval/evidence_subgraph.py +63 -0
  127. structverify/retrieval/kosis_connector.py +1192 -0
  128. structverify/retrieval/kosis_relevance.py +142 -0
  129. structverify/retrieval/kosis_source.py +1541 -0
  130. structverify/retrieval/query_builder.py +72 -0
  131. structverify/retrieval/registry.py +133 -0
  132. structverify/retrieval/relevance_judge.py +141 -0
  133. structverify/retrieval/row_matcher.py +267 -0
  134. structverify/storage/__init__.py +0 -0
  135. structverify/storage/db_manager.py +157 -0
  136. structverify/storage/dwh_manager.py +92 -0
  137. structverify/storage/init_db.py +99 -0
  138. structverify/storage/raw_storage.py +29 -0
  139. structverify/training/__init__.py +26 -0
  140. structverify/training/curator.py +124 -0
  141. structverify/training/dataset.py +134 -0
  142. structverify/training/doctor.py +99 -0
  143. structverify/training/evalgate.py +96 -0
  144. structverify/training/generate.py +101 -0
  145. structverify/training/loop.py +116 -0
  146. structverify/training/recipe/train_mlx.py +99 -0
  147. structverify/training/recipe/train_qlora.py +104 -0
  148. structverify/training/tasks.py +79 -0
  149. structverify/utils/__init__.py +0 -0
  150. structverify/utils/embedding_client.py +248 -0
  151. structverify/utils/llm_client.py +809 -0
  152. structverify/utils/logger.py +81 -0
  153. structverify/verification/__init__.py +0 -0
  154. structverify/verification/_config.py +45 -0
  155. structverify/verification/adapters.py +405 -0
  156. structverify/verification/conformance.py +117 -0
  157. structverify/verification/decide_verdict.py +216 -0
  158. structverify/verification/decide_verdict_agent.py +454 -0
  159. structverify/verification/growth_diff.py +267 -0
  160. structverify/verification/row_match.py +345 -0
  161. structverify/verification/units.py +64 -0
  162. structverify/verification/verdict_thresholds.py +232 -0
  163. structverify/verification/verifier.py +84 -0
  164. structverify-0.3.0.dist-info/METADATA +903 -0
  165. structverify-0.3.0.dist-info/RECORD +168 -0
  166. structverify-0.3.0.dist-info/WHEEL +5 -0
  167. structverify-0.3.0.dist-info/licenses/LICENSE +21 -0
  168. structverify-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,240 @@
1
+ """
2
+ preprocessing/sir_builder.py — SIR Tree 빌더 (v1: 기본 정제 + GraphRAG 문맥 엣지)
3
+
4
+ [김예슬 - 2026-04-24]
5
+ - _clean_text_basic() 추가:
6
+ · URL 제거, 연속 빈줄 정리, 연속 공백 정리 (최소 정제만)
7
+ · LLM 정제는 추후 use_llm_clean: true 옵션으로 활성화 예정
8
+ - 절대 char_offset 보정:
9
+ · 기존: segmenter 상대 offset 그대로 사용 (버그)
10
+ · 수정: block_start + 상대 offset → 절대 offset
11
+ - 전역 sent_id 부여:
12
+ · 기존: s0000, s0001 (블록 내 중복 가능)
13
+ · 수정: b0000s0000, b0001s0000 (문서 전체 유일)
14
+ - _detect_block_type(): LIST, TABLE 감지 추가
15
+ - extract_context_edges(): GraphRAG용 문맥 엣지 추출 추가
16
+ · NEXT_SENT: 문장 간 순서 (문맥 흐름 보존)
17
+ · IN_BLOCK: 문장 → 소속 문단
18
+ · IN_DOC: 문단 → 소속 문서
19
+ → graph_builder.py에서 Neo4j에 저장하면
20
+ 2-hop 탐색으로 "같은 기사 내 연관 수치" 발견 가능
21
+
22
+ [참고] Docling (IBM Research, 2024) — https://github.com/DS4SD/docling
23
+ """
24
+ from __future__ import annotations
25
+
26
+ import re
27
+ from typing import Any
28
+
29
+ from structverify.core.schemas import (
30
+ BlockType, SIRBlock, SIRDocument, SourceOffset, SourceType,
31
+ )
32
+ from structverify.preprocessing.segmenter import split_sentences
33
+ from structverify.utils.logger import get_logger
34
+
35
+ logger = get_logger(__name__)
36
+
37
+ # ── 기본 정제 패턴 ────────────────────────────────────────────────────────
38
+ _URL_RE = re.compile(r'https?://\S+')
39
+ _BLANK_LINES = re.compile(r'\n{3,}')
40
+ _SPACES = re.compile(r'[ \t]{2,}')
41
+
42
+ # ════════════════════════════════════════════════════════════════════════
43
+ # 메인 빌더
44
+ # ════════════════════════════════════════════════════════════════════════
45
+
46
+ def build_sir(
47
+ raw_text: str,
48
+ source_type: SourceType,
49
+ source_uri: str | None = None,
50
+ ) -> SIRDocument:
51
+ """
52
+ MD 텍스트 → SIRDocument 변환.
53
+
54
+ 처리 순서:
55
+ 1) _clean_text_basic(): URL 제거, 빈줄 정리 (최소 정제)
56
+ 2) \\n{2,} 기준 문단 분할
57
+ 3) 블록 타입 감지 (HEADING / PARAGRAPH / LIST / TABLE)
58
+ 4) kss 문장 분리 + 절대 offset 보정
59
+ 5) graph_anchor_id 부여 (블록/문장 모두)
60
+
61
+ LLM 기반 심층 정제가 필요하면 추후 build_sir_async() 사용.
62
+ """
63
+ cleaned = _clean_text_basic(raw_text)
64
+
65
+ blocks: list[SIRBlock] = []
66
+ search_pos = 0
67
+ block_seq = 0
68
+
69
+ for para in re.split(r'\n{2,}', cleaned):
70
+ para_stripped = para.strip()
71
+
72
+ # 빈 문단 스킵
73
+ if not para_stripped or len(para_stripped) < 2:
74
+ search_pos += len(para) + 2
75
+ continue
76
+
77
+ block_type, level = _detect_block_type(para_stripped)
78
+
79
+ # 절대 시작 위치 계산
80
+ block_start = cleaned.find(para_stripped, search_pos)
81
+ if block_start < 0:
82
+ block_start = search_pos
83
+ block_end = block_start + len(para_stripped)
84
+
85
+ block_id = f"b{block_seq:04d}"
86
+ block_anchor = f"node:{block_id}"
87
+
88
+ # 문장 분리 + 절대 offset 보정
89
+ sentences = split_sentences(para_stripped)
90
+ for sent in sentences:
91
+ # 전역 유일 sent_id (블록ID + 문장ID 조합)
92
+ global_sent_id = f"{block_id}_{sent.sent_id}"
93
+ sent.sent_id = global_sent_id
94
+ sent.graph_anchor_id = f"node:{global_sent_id}"
95
+ # 상대 offset → 절대 offset
96
+ sent.char_offset_start = block_start + sent.char_offset_start
97
+ sent.char_offset_end = block_start + sent.char_offset_end
98
+
99
+ block = SIRBlock(
100
+ block_id=block_id,
101
+ type=block_type,
102
+ level=level,
103
+ content=para_stripped,
104
+ sentences=sentences,
105
+ graph_anchor_ids=[block_anchor],
106
+ entity_refs=_extract_entity_refs(para_stripped),
107
+ event_refs=_extract_event_refs(para_stripped),
108
+ source_offset=SourceOffset(
109
+ char_start=block_start,
110
+ char_end=block_end,
111
+ ),
112
+ )
113
+ blocks.append(block)
114
+ search_pos = block_end
115
+ block_seq += 1
116
+
117
+ doc = SIRDocument(
118
+ source_type=source_type,
119
+ source_uri=source_uri,
120
+ blocks=blocks,
121
+ )
122
+ total_sents = sum(len(b.sentences) for b in blocks)
123
+ logger.info(f"SIR Tree: {len(blocks)} blocks, {total_sents} sentences [{source_type.value}]")
124
+ return doc
125
+
126
+
127
+ # ════════════════════════════════════════════════════════════════════════
128
+ # GraphRAG 문맥 엣지
129
+ # ════════════════════════════════════════════════════════════════════════
130
+
131
+ def extract_context_edges(doc: SIRDocument) -> list[dict[str, Any]]:
132
+ """
133
+ GraphRAG용 문맥 엣지 추출.
134
+
135
+ graph_builder.py에서 이 함수를 호출하여 Neo4j에 저장하면
136
+ 2-hop 탐색으로 "같은 기사 내 연관 수치"를 발견할 수 있다.
137
+
138
+ 엣지 유형:
139
+ NEXT_SENT : 문장 → 다음 문장 (문맥 흐름 보존)
140
+ IN_BLOCK : 문장 → 소속 문단
141
+ IN_DOC : 문단 → 소속 문서
142
+
143
+ TODO [이수민]: graph_builder.py에서 이 함수 호출 후 Neo4j 저장
144
+ """
145
+ edges: list[dict[str, Any]] = []
146
+ doc_node = f"node:doc:{doc.doc_id.hex[:8]}"
147
+
148
+ for block in doc.blocks:
149
+ block_anchor = block.graph_anchor_ids[0] if block.graph_anchor_ids else None
150
+ if not block_anchor:
151
+ continue
152
+
153
+ # IN_DOC: 문단 → 문서
154
+ edges.append({
155
+ "from_node": block_anchor,
156
+ "to_node": doc_node,
157
+ "edge_type": "IN_DOC",
158
+ })
159
+
160
+ sents = block.sentences
161
+ for i, sent in enumerate(sents):
162
+ if not sent.graph_anchor_id:
163
+ continue
164
+
165
+ # IN_BLOCK: 문장 → 문단
166
+ edges.append({
167
+ "from_node": sent.graph_anchor_id,
168
+ "to_node": block_anchor,
169
+ "edge_type": "IN_BLOCK",
170
+ })
171
+
172
+ # NEXT_SENT: 문장 → 다음 문장
173
+ if i + 1 < len(sents) and sents[i + 1].graph_anchor_id:
174
+ edges.append({
175
+ "from_node": sent.graph_anchor_id,
176
+ "to_node": sents[i + 1].graph_anchor_id,
177
+ "edge_type": "NEXT_SENT",
178
+ })
179
+
180
+ logger.debug(f"문맥 엣지 {len(edges)}개 추출")
181
+ return edges
182
+
183
+
184
+ # ════════════════════════════════════════════════════════════════════════
185
+ # 내부 유틸
186
+ # ════════════════════════════════════════════════════════════════════════
187
+
188
+ def _clean_text_basic(raw: str) -> str:
189
+ """
190
+ 최소한의 공통 정제.
191
+ 어떤 소스든 확실히 해당되는 것만 처리.
192
+
193
+ - URL 제거
194
+ - 연속 공백(탭) → 단일 공백
195
+ - 3줄 이상 빈 줄 → 2줄
196
+
197
+ 노이즈 심화 정제(페이지번호, 이미지섹션 등)는
198
+ 추후 LLM 기반 clean_text_with_llm()으로 처리 예정.
199
+ """
200
+ text = _URL_RE.sub('', raw)
201
+ text = _SPACES.sub(' ', text)
202
+ text = _BLANK_LINES.sub('\n\n', text)
203
+ return text.strip()
204
+
205
+
206
+ def _detect_block_type(text: str) -> tuple[BlockType, int | None]:
207
+ """MD 블록 타입 감지."""
208
+ stripped = text.strip()
209
+
210
+ # 헤딩: # ~ ######
211
+ m = re.match(r'^(#{1,6})\s+', stripped)
212
+ if m:
213
+ return BlockType.HEADING, len(m.group(1))
214
+
215
+ # 테이블: | 로 시작
216
+ if stripped.startswith('|') and '|' in stripped:
217
+ return BlockType.TABLE, None
218
+
219
+ # 목록: -, *, 숫자.
220
+ if re.match(r'^[-*]\s+', stripped) or re.match(r'^\d+\.\s+', stripped):
221
+ return BlockType.LIST, None
222
+
223
+ return BlockType.PARAGRAPH, None
224
+
225
+
226
+ def _extract_entity_refs(text: str) -> list[str]:
227
+ """
228
+ 기관명/단체명 추출.
229
+ 현재 구현 안함.
230
+ TODO : 김예슬
231
+ """
232
+ return []
233
+
234
+
235
+ def _extract_event_refs(text: str) -> list[str]:
236
+ """날짜/시점 표현 추출.
237
+ 현재 구현 안함.
238
+ TODO : 김예슬
239
+ """
240
+ return []