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,227 @@
1
+ """structverify.agent.reflect — Reflect Agent (Phase E).
2
+
3
+ ReAct 패턴의 *Reflect* 단계:
4
+ 매 iter 시작 시 LLM이 last_observation 보고 *다음 action 동적 결정*.
5
+
6
+ Phase D의 deterministic plan 따르기 → Phase E의 LLM-driven 결정.
7
+ 룰베이스 (_select_best_row, _infer_claim_type 등)는 *fallback only*.
8
+
9
+ 사용:
10
+ from structverify.agent.reflect import ReflectAgent
11
+
12
+ reflect = ReflectAgent(llm_call=my_llm_call, config={...})
13
+ verdict = await agent_loop(
14
+ plan=plan, claim=claim, workspace=ws, datasources=ds,
15
+ reflect_fn=reflect, # callable, agent_loop이 매 iter 호출
16
+ loop_config=LoopConfig(mode="reflect"),
17
+ )
18
+ """
19
+ from __future__ import annotations
20
+
21
+ import json
22
+ import re
23
+ from dataclasses import dataclass
24
+ from typing import Any, Awaitable, Callable
25
+
26
+ from structverify.utils.logger import get_logger
27
+ from .schemas import ActionType, Observation, Plan, ReflectDecision, VerdictType
28
+ from .prompts.reflect_prompts import build_reflect_prompt
29
+
30
+ logger = get_logger(__name__)
31
+
32
+
33
+ # ── JSON 추출 (planner와 동일 패턴) ──────────────────────────────
34
+
35
+ _JSON_FENCE_RE = re.compile(r"```(?:json)?\s*\n?(.*?)\n?```", re.DOTALL)
36
+ _JSON_OBJ_RE = re.compile(r"\{.*\}", re.DOTALL)
37
+
38
+ # JSON 표준엔 주석 없는데 LLM이 자주 박음 — 파싱 전 제거
39
+ _LINE_COMMENT_RE = re.compile(r"//[^\n\r]*")
40
+ _BLOCK_COMMENT_RE = re.compile(r"/\*.*?\*/", re.DOTALL)
41
+ # trailing comma도 흔함
42
+ _TRAILING_COMMA_RE = re.compile(r",\s*([}\]])")
43
+
44
+
45
+ def _clean_json_text(text: str) -> str:
46
+ """LLM 응답에서 JSON 표준 위반 흔한 패턴 제거."""
47
+ text = _BLOCK_COMMENT_RE.sub("", text)
48
+ text = _LINE_COMMENT_RE.sub("", text)
49
+ text = _TRAILING_COMMA_RE.sub(r"\1", text)
50
+ return text
51
+
52
+
53
+ def _extract_json(text: str) -> dict | None:
54
+ """LLM 응답에서 JSON object 추출. fenced ``` 블록 우선, 그 다음 첫 { ... }."""
55
+ if not text:
56
+ return None
57
+
58
+ # 1. fenced code block
59
+ m = _JSON_FENCE_RE.search(text)
60
+ if m:
61
+ body = _clean_json_text(m.group(1).strip())
62
+ try:
63
+ return json.loads(body)
64
+ except json.JSONDecodeError:
65
+ pass
66
+
67
+ # 2. 첫 { ... } 매칭
68
+ m = _JSON_OBJ_RE.search(text)
69
+ if m:
70
+ body = _clean_json_text(m.group(0))
71
+ try:
72
+ return json.loads(body)
73
+ except json.JSONDecodeError:
74
+ pass
75
+
76
+ # 3. 전체 text
77
+ try:
78
+ return json.loads(_clean_json_text(text.strip()))
79
+ except json.JSONDecodeError:
80
+ return None
81
+
82
+
83
+ def _parse_reflect_decision(response_text: str) -> ReflectDecision | None:
84
+ """LLM 응답 → ReflectDecision. 실패 시 None (호출자가 fallback)."""
85
+ data = _extract_json(response_text)
86
+ if not data or not isinstance(data, dict):
87
+ logger.warning(f"[reflect] JSON 추출 실패. 응답 일부: {response_text[:300]!r}")
88
+ return None
89
+
90
+ # action 파싱
91
+ raw_action = (data.get("action") or "").strip().lower()
92
+ action_map = {
93
+ "catalog_search": ActionType.CATALOG_SEARCH,
94
+ "explore_catalog": ActionType.EXPLORE_CATALOG,
95
+ "fetch_evidence": ActionType.FETCH_EVIDENCE,
96
+ "read_original": ActionType.READ_ORIGINAL,
97
+ "calculate": ActionType.CALCULATE,
98
+ "replan": ActionType.REPLAN,
99
+ "finish": ActionType.FINISH,
100
+ }
101
+ action = action_map.get(raw_action)
102
+ if action is None:
103
+ logger.warning(f"[reflect] 알 수 없는 action: {raw_action!r}")
104
+ return None
105
+
106
+ # input
107
+ inp = data.get("input") or {}
108
+ if not isinstance(inp, dict):
109
+ logger.warning(f"[reflect] input이 dict 아님: {type(inp).__name__}")
110
+ inp = {}
111
+
112
+ # confidence
113
+ try:
114
+ conf = float(data.get("confidence_so_far", 0.0))
115
+ except (TypeError, ValueError):
116
+ conf = 0.0
117
+ conf = max(0.0, min(1.0, conf))
118
+
119
+ # proposed_verdict (finish action에서만 의미)
120
+ raw_verdict = data.get("proposed_verdict")
121
+ verdict_obj: VerdictType | None = None
122
+ if raw_verdict:
123
+ try:
124
+ verdict_obj = VerdictType(str(raw_verdict).strip().lower())
125
+ except ValueError:
126
+ logger.debug(f"[reflect] proposed_verdict 파싱 실패: {raw_verdict!r}")
127
+
128
+ return ReflectDecision(
129
+ thought=str(data.get("thought") or "").strip(),
130
+ action=action,
131
+ input=inp,
132
+ confidence_so_far=conf,
133
+ proposed_verdict=verdict_obj,
134
+ proposed_explanation=(data.get("proposed_explanation") or None),
135
+ )
136
+
137
+
138
+ # ── ReflectAgent ─────────────────────────────────────────────────
139
+
140
+ @dataclass
141
+ class ReflectConfig:
142
+ model_tier: str = "light"
143
+ """LLM 모델 선택. heavy는 비용 큼."""
144
+ temperature: float = 0.2
145
+ """낮을수록 일관적. 0.2~0.3 권장."""
146
+ max_retries: int = 1
147
+ """JSON 파싱 실패 시 retry 횟수."""
148
+
149
+
150
+ class ReflectAgent:
151
+ """LLM 호출 → ReflectDecision 반환.
152
+
153
+ agent_loop의 reflect_fn 시그니처에 맞춤:
154
+ async __call__(plan, memory_text, last_observation, iter_num) -> ReflectDecision | None
155
+
156
+ 실패 시 None 반환 → loop이 deterministic fallback (plan의 다음 step).
157
+ """
158
+
159
+ def __init__(
160
+ self,
161
+ llm_call: Callable[..., Awaitable[str]],
162
+ claim: Any,
163
+ config: ReflectConfig | None = None,
164
+ max_iterations: int = 10,
165
+ ):
166
+ """
167
+ Args:
168
+ llm_call: async function. signature: `async (prompt: str) -> str`.
169
+ (보통 lambda로 model_tier/temperature를 binding)
170
+ claim: 현재 claim. schema 정보 접근용.
171
+ config: ReflectConfig
172
+ max_iterations: prompt 안에 표시할 max iter
173
+ """
174
+ self.llm_call = llm_call
175
+ self.claim = claim
176
+ self.config = config or ReflectConfig()
177
+ self.max_iterations = max_iterations
178
+ self._call_count = 0
179
+
180
+ async def __call__(
181
+ self,
182
+ plan: Plan,
183
+ memory_text: str,
184
+ last_observation: Observation | None,
185
+ iter_num: int,
186
+ ) -> ReflectDecision | None:
187
+ """매 iter 호출됨. ReflectDecision 또는 None (fallback)."""
188
+ self._call_count += 1
189
+
190
+ prompt = build_reflect_prompt(
191
+ claim=self.claim,
192
+ plan=plan,
193
+ memory_text=memory_text,
194
+ last_observation=last_observation,
195
+ iter_num=iter_num,
196
+ max_iterations=self.max_iterations,
197
+ )
198
+
199
+ for attempt in range(1, self.config.max_retries + 2): # 최초 1회 + retry
200
+ try:
201
+ response = await self.llm_call(prompt)
202
+ except Exception as e:
203
+ logger.warning(
204
+ f"[reflect] LLM 호출 실패 (iter={iter_num}, attempt={attempt}): "
205
+ f"{type(e).__name__}: {e}"
206
+ )
207
+ continue
208
+
209
+ decision = _parse_reflect_decision(response)
210
+ if decision is not None:
211
+ logger.info(
212
+ f"[reflect] iter {iter_num}: action={decision.action.value} "
213
+ f"thought={decision.thought[:120]!r} "
214
+ f"confidence={decision.confidence_so_far:.2f}"
215
+ )
216
+ return decision
217
+
218
+ logger.warning(
219
+ f"[reflect] iter {iter_num} attempt {attempt}: 파싱 실패. "
220
+ f"응답 일부: {response[:200]!r}"
221
+ )
222
+
223
+ # 모든 시도 실패 → None (loop이 deterministic fallback)
224
+ logger.warning(
225
+ f"[reflect] iter {iter_num}: 모든 시도 실패, deterministic fallback"
226
+ )
227
+ return None