python-hwpx-automation 6.0.3__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 (217) hide show
  1. hwpx_automation/__init__.py +61 -0
  2. hwpx_automation/__init__.pyi +27 -0
  3. hwpx_automation/__main__.py +8 -0
  4. hwpx_automation/agent_document.py +392 -0
  5. hwpx_automation/api.py +136 -0
  6. hwpx_automation/blind_eval.py +407 -0
  7. hwpx_automation/capabilities.py +110 -0
  8. hwpx_automation/compat.py +48 -0
  9. hwpx_automation/configuration.py +60 -0
  10. hwpx_automation/core/__init__.py +2 -0
  11. hwpx_automation/core/content.py +762 -0
  12. hwpx_automation/core/context.py +111 -0
  13. hwpx_automation/core/diff.py +53 -0
  14. hwpx_automation/core/document.py +37 -0
  15. hwpx_automation/core/formatting.py +513 -0
  16. hwpx_automation/core/handles.py +24 -0
  17. hwpx_automation/core/locations.py +205 -0
  18. hwpx_automation/core/locator.py +162 -0
  19. hwpx_automation/core/plan.py +680 -0
  20. hwpx_automation/core/resources.py +42 -0
  21. hwpx_automation/core/search.py +296 -0
  22. hwpx_automation/core/transactions.py +434 -0
  23. hwpx_automation/core/txn.py +48 -0
  24. hwpx_automation/document_state.py +95 -0
  25. hwpx_automation/errors.py +174 -0
  26. hwpx_automation/execution_lock.py +15 -0
  27. hwpx_automation/fastmcp_adapter.py +672 -0
  28. hwpx_automation/form_fill.py +1177 -0
  29. hwpx_automation/form_output_models.py +223 -0
  30. hwpx_automation/handlers/__init__.py +2 -0
  31. hwpx_automation/handlers/_shared.py +377 -0
  32. hwpx_automation/handlers/agent_document.py +257 -0
  33. hwpx_automation/handlers/authoring.py +750 -0
  34. hwpx_automation/handlers/content_edit.py +1078 -0
  35. hwpx_automation/handlers/form_fill.py +607 -0
  36. hwpx_automation/handlers/layout_style.py +660 -0
  37. hwpx_automation/handlers/quality_render.py +566 -0
  38. hwpx_automation/handlers/read_export.py +1295 -0
  39. hwpx_automation/handlers/specialized.py +624 -0
  40. hwpx_automation/handlers/tracked_changes.py +589 -0
  41. hwpx_automation/handlers/workflow.py +105 -0
  42. hwpx_automation/hwp_converter.py +227 -0
  43. hwpx_automation/hwp_support.py +94 -0
  44. hwpx_automation/hwpx_ops.py +1439 -0
  45. hwpx_automation/identity.json +263 -0
  46. hwpx_automation/identity.py +18 -0
  47. hwpx_automation/ingest_adapters.py +85 -0
  48. hwpx_automation/markdown_plan.py +216 -0
  49. hwpx_automation/mcp_cli.py +29 -0
  50. hwpx_automation/metadata/tools_meta.py +40 -0
  51. hwpx_automation/mixed_form.py +3007 -0
  52. hwpx_automation/mutation_models.py +401 -0
  53. hwpx_automation/network_policy.py +232 -0
  54. hwpx_automation/office/__init__.py +14 -0
  55. hwpx_automation/office/agent/__init__.py +125 -0
  56. hwpx_automation/office/agent/_batch_verification.py +383 -0
  57. hwpx_automation/office/agent/blueprint/__init__.py +58 -0
  58. hwpx_automation/office/agent/blueprint/bundle.py +282 -0
  59. hwpx_automation/office/agent/blueprint/catalog.py +136 -0
  60. hwpx_automation/office/agent/blueprint/dump.py +520 -0
  61. hwpx_automation/office/agent/blueprint/mapping.py +312 -0
  62. hwpx_automation/office/agent/blueprint/model.py +722 -0
  63. hwpx_automation/office/agent/blueprint/native.py +621 -0
  64. hwpx_automation/office/agent/blueprint/replay.py +622 -0
  65. hwpx_automation/office/agent/catalog.py +252 -0
  66. hwpx_automation/office/agent/cli.py +647 -0
  67. hwpx_automation/office/agent/commands.py +1383 -0
  68. hwpx_automation/office/agent/document.py +801 -0
  69. hwpx_automation/office/agent/form_plan.py +1760 -0
  70. hwpx_automation/office/agent/model.py +808 -0
  71. hwpx_automation/office/agent/path.py +155 -0
  72. hwpx_automation/office/agent/query.py +230 -0
  73. hwpx_automation/office/agent/story.py +207 -0
  74. hwpx_automation/office/authoring/__init__.py +3542 -0
  75. hwpx_automation/office/authoring/advanced_generators.py +154 -0
  76. hwpx_automation/office/authoring/builder/__init__.py +52 -0
  77. hwpx_automation/office/authoring/builder/core.py +996 -0
  78. hwpx_automation/office/authoring/builder/report.py +195 -0
  79. hwpx_automation/office/authoring/design/__init__.py +30 -0
  80. hwpx_automation/office/authoring/design/_support.py +144 -0
  81. hwpx_automation/office/authoring/design/composer.py +282 -0
  82. hwpx_automation/office/authoring/design/harvest.py +305 -0
  83. hwpx_automation/office/authoring/design/plan.py +69 -0
  84. hwpx_automation/office/authoring/design/profile.py +88 -0
  85. hwpx_automation/office/authoring/design/profiles/application_form/fragments/body.xml +1 -0
  86. hwpx_automation/office/authoring/design/profiles/application_form/fragments/heading.xml +1 -0
  87. hwpx_automation/office/authoring/design/profiles/application_form/fragments/info_table.xml +1 -0
  88. hwpx_automation/office/authoring/design/profiles/application_form/fragments/title.xml +1 -0
  89. hwpx_automation/office/authoring/design/profiles/application_form/profile.json +25 -0
  90. hwpx_automation/office/authoring/design/profiles/application_form/template.hwpx +0 -0
  91. hwpx_automation/office/authoring/design/profiles/home_notice/fragments/body.xml +1 -0
  92. hwpx_automation/office/authoring/design/profiles/home_notice/fragments/heading.xml +1 -0
  93. hwpx_automation/office/authoring/design/profiles/home_notice/fragments/title.xml +1 -0
  94. hwpx_automation/office/authoring/design/profiles/home_notice/profile.json +24 -0
  95. hwpx_automation/office/authoring/design/profiles/home_notice/template.hwpx +0 -0
  96. hwpx_automation/office/authoring/design/profiles/official_notice/fragments/body.xml +1 -0
  97. hwpx_automation/office/authoring/design/profiles/official_notice/fragments/heading.xml +1 -0
  98. hwpx_automation/office/authoring/design/profiles/official_notice/fragments/info_table.xml +1 -0
  99. hwpx_automation/office/authoring/design/profiles/official_notice/fragments/title.xml +1 -0
  100. hwpx_automation/office/authoring/design/profiles/official_notice/profile.json +25 -0
  101. hwpx_automation/office/authoring/design/profiles/official_notice/template.hwpx +0 -0
  102. hwpx_automation/office/authoring/design/profiles/report/fragments/body.xml +1 -0
  103. hwpx_automation/office/authoring/design/profiles/report/fragments/heading.xml +1 -0
  104. hwpx_automation/office/authoring/design/profiles/report/fragments/info_table.xml +1 -0
  105. hwpx_automation/office/authoring/design/profiles/report/fragments/title.xml +1 -0
  106. hwpx_automation/office/authoring/design/profiles/report/profile.json +25 -0
  107. hwpx_automation/office/authoring/design/profiles/report/template.hwpx +0 -0
  108. hwpx_automation/office/authoring/design/validator.py +107 -0
  109. hwpx_automation/office/authoring/presets/__init__.py +22 -0
  110. hwpx_automation/office/authoring/presets/proposal.py +538 -0
  111. hwpx_automation/office/authoring/report_parser.py +141 -0
  112. hwpx_automation/office/authoring/style_profile.py +437 -0
  113. hwpx_automation/office/authoring/template_analyzer.py +657 -0
  114. hwpx_automation/office/compliance/__init__.py +38 -0
  115. hwpx_automation/office/compliance/official_lint.py +478 -0
  116. hwpx_automation/office/compliance/pii.py +388 -0
  117. hwpx_automation/office/document_ops/__init__.py +13 -0
  118. hwpx_automation/office/document_ops/comparison.py +62 -0
  119. hwpx_automation/office/document_ops/mail_merge.py +73 -0
  120. hwpx_automation/office/document_ops/redline.py +35 -0
  121. hwpx_automation/office/evalplan/__init__.py +36 -0
  122. hwpx_automation/office/evalplan/runtime.py +2762 -0
  123. hwpx_automation/office/exam/__init__.py +44 -0
  124. hwpx_automation/office/exam/compose.py +282 -0
  125. hwpx_automation/office/exam/ir.py +44 -0
  126. hwpx_automation/office/exam/measure.py +163 -0
  127. hwpx_automation/office/exam/parser.py +150 -0
  128. hwpx_automation/office/exam/profile.py +123 -0
  129. hwpx_automation/office/form_fill/__init__.py +66 -0
  130. hwpx_automation/office/form_fill/classification.py +108 -0
  131. hwpx_automation/office/form_fill/fill_residue.py +242 -0
  132. hwpx_automation/office/form_fill/fit/__init__.py +36 -0
  133. hwpx_automation/office/form_fill/fit/apply.py +24 -0
  134. hwpx_automation/office/form_fill/fit/engine.py +24 -0
  135. hwpx_automation/office/form_fill/fit/measure.py +50 -0
  136. hwpx_automation/office/form_fill/fit/policy.py +28 -0
  137. hwpx_automation/office/form_fill/fit/report.py +28 -0
  138. hwpx_automation/office/form_fill/fit/seal.py +457 -0
  139. hwpx_automation/office/form_fill/fit/wordbox.py +1343 -0
  140. hwpx_automation/office/form_fill/guidance.py +704 -0
  141. hwpx_automation/office/form_fill/quality.py +961 -0
  142. hwpx_automation/office/form_fill/split_run.py +333 -0
  143. hwpx_automation/office/form_fill/template_formfit.py +656 -0
  144. hwpx_automation/office/house_style/__init__.py +196 -0
  145. hwpx_automation/office/house_style/composition.py +68 -0
  146. hwpx_automation/office/house_style/data/bank.json +625 -0
  147. hwpx_automation/office/house_style/data/genres.json +43 -0
  148. hwpx_automation/office/quality/__init__.py +14 -0
  149. hwpx_automation/office/quality/page_guard.py +277 -0
  150. hwpx_automation/office/rendering/__init__.py +145 -0
  151. hwpx_automation/office/rendering/_hancom_open_rate.ps1 +374 -0
  152. hwpx_automation/office/rendering/_refresh_hwpx_mac.applescript +162 -0
  153. hwpx_automation/office/rendering/_render_hwpx.ps1 +72 -0
  154. hwpx_automation/office/rendering/_render_hwpx_mac.applescript +249 -0
  155. hwpx_automation/office/rendering/block_splits.py +76 -0
  156. hwpx_automation/office/rendering/detectors.py +151 -0
  157. hwpx_automation/office/rendering/diff.py +153 -0
  158. hwpx_automation/office/rendering/fixture_corpus.py +215 -0
  159. hwpx_automation/office/rendering/oracle.py +909 -0
  160. hwpx_automation/office/rendering/page_qa.py +245 -0
  161. hwpx_automation/office/rendering/qa_contracts.py +293 -0
  162. hwpx_automation/office/rendering/qa_metrics.py +241 -0
  163. hwpx_automation/office/rendering/worker.py +290 -0
  164. hwpx_automation/office/utilities/__init__.py +12 -0
  165. hwpx_automation/office/utilities/table_compute.py +477 -0
  166. hwpx_automation/ops_services/__init__.py +1 -0
  167. hwpx_automation/ops_services/_border_fill.py +283 -0
  168. hwpx_automation/ops_services/composition.py +55 -0
  169. hwpx_automation/ops_services/content_layout.py +322 -0
  170. hwpx_automation/ops_services/context.py +213 -0
  171. hwpx_automation/ops_services/form_fields.py +557 -0
  172. hwpx_automation/ops_services/media.py +178 -0
  173. hwpx_automation/ops_services/memo_style.py +477 -0
  174. hwpx_automation/ops_services/package_validation.py +166 -0
  175. hwpx_automation/ops_services/planning.py +201 -0
  176. hwpx_automation/ops_services/preview_export.py +585 -0
  177. hwpx_automation/ops_services/read_query.py +601 -0
  178. hwpx_automation/ops_services/save_policy.py +604 -0
  179. hwpx_automation/ops_services/tables.py +539 -0
  180. hwpx_automation/ops_services/transactions.py +616 -0
  181. hwpx_automation/preview_output_models.py +69 -0
  182. hwpx_automation/public-modules.json +206 -0
  183. hwpx_automation/py.typed +1 -0
  184. hwpx_automation/quality.py +351 -0
  185. hwpx_automation/quality_generation.py +725 -0
  186. hwpx_automation/runtime.py +321 -0
  187. hwpx_automation/runtime_services.py +100 -0
  188. hwpx_automation/server.py +259 -0
  189. hwpx_automation/storage.py +747 -0
  190. hwpx_automation/tool_bindings.py +170 -0
  191. hwpx_automation/tool_contract.py +982 -0
  192. hwpx_automation/upstream.py +755 -0
  193. hwpx_automation/utils/__init__.py +2 -0
  194. hwpx_automation/utils/helpers.py +29 -0
  195. hwpx_automation/visual_qa.py +667 -0
  196. hwpx_automation/workflow/__init__.py +55 -0
  197. hwpx_automation/workflow/adapters.py +482 -0
  198. hwpx_automation/workflow/dispatcher.py +213 -0
  199. hwpx_automation/workflow/models.py +243 -0
  200. hwpx_automation/workflow/policy.py +198 -0
  201. hwpx_automation/workflow/render_contracts.py +173 -0
  202. hwpx_automation/workflow/render_metrics.py +196 -0
  203. hwpx_automation/workflow/render_queue.py +482 -0
  204. hwpx_automation/workflow/render_security.py +172 -0
  205. hwpx_automation/workflow/render_transport.py +369 -0
  206. hwpx_automation/workflow/rendering.py +206 -0
  207. hwpx_automation/workflow/service.py +758 -0
  208. hwpx_automation/workflow/state_machine.py +65 -0
  209. hwpx_automation/workflow/store.py +747 -0
  210. hwpx_automation/workspace.py +1694 -0
  211. python_hwpx_automation-6.0.3.dist-info/METADATA +279 -0
  212. python_hwpx_automation-6.0.3.dist-info/RECORD +217 -0
  213. python_hwpx_automation-6.0.3.dist-info/WHEEL +5 -0
  214. python_hwpx_automation-6.0.3.dist-info/entry_points.txt +3 -0
  215. python_hwpx_automation-6.0.3.dist-info/licenses/LICENSE +178 -0
  216. python_hwpx_automation-6.0.3.dist-info/licenses/NOTICE +14 -0
  217. python_hwpx_automation-6.0.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,166 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ from __future__ import annotations
4
+
5
+ import dataclasses
6
+ import logging
7
+ import re
8
+ from dataclasses import asdict
9
+ from typing import Any, Dict, List, Optional, Sequence, cast
10
+ from xml.etree import ElementTree as ET
11
+
12
+ from ..upstream import (
13
+ ValidationReport,
14
+ create_text_extractor,
15
+ open_package,
16
+ validate_document_path,
17
+ )
18
+
19
+ from .context import DocumentContext
20
+
21
+ logger = logging.getLogger("hwpx_automation.hwpx_ops")
22
+
23
+
24
+ class PackageValidationService:
25
+ def __init__(self, context: DocumentContext) -> None:
26
+ self._context = context
27
+
28
+ def package_parts(self, path: str) -> Dict[str, Any]:
29
+ resolved = self._context._resolve_path(path)
30
+ package = open_package(resolved)
31
+ parts = sorted(package.part_names())
32
+ return {"parts": parts}
33
+
34
+ def package_get_text(
35
+ self, path: str, part_name: str, encoding: str | None = None
36
+ ) -> Dict[str, Any]:
37
+ resolved = self._context._resolve_path(path)
38
+ package = open_package(resolved)
39
+ text = package.get_text(part_name, encoding=encoding or "utf-8")
40
+ return {"text": text}
41
+
42
+ def repair_hwpx(
43
+ self,
44
+ source: str,
45
+ output: str,
46
+ *,
47
+ recover: bool = False,
48
+ overwrite: bool = False,
49
+ max_entry_size: int = 64 * 1024 * 1024,
50
+ max_total_size: int = 512 * 1024 * 1024,
51
+ max_source_size: int = 512 * 1024 * 1024,
52
+ ) -> Dict[str, Any]:
53
+ try:
54
+ from hwpx.tools.package_validator import validate_editor_open_safety
55
+ from hwpx.tools.package_validator import validate_package
56
+ from hwpx.tools.repair import repair_from_recovered, repair_repack
57
+ except Exception as exc: # pragma: no cover - depends on installed python-hwpx
58
+ raise self._context._new_error(
59
+ "REPAIR_UNAVAILABLE",
60
+ "python-hwpx repair support is not available; install a python-hwpx build with hwpx.tools.repair",
61
+ hint="Upgrade python-hwpx to a version that includes hwpx.tools.repair and hwpx.tools.recover.",
62
+ ) from exc
63
+
64
+ source_path = self._context._resolve_path(source)
65
+ output_path = self._context.storage.resolve_output_path(output)
66
+ if recover:
67
+ result = repair_from_recovered(
68
+ source_path,
69
+ output_path,
70
+ overwrite=overwrite,
71
+ max_entry_size=max_entry_size,
72
+ max_total_size=max_total_size,
73
+ max_source_size=max_source_size,
74
+ )
75
+ else:
76
+ result = repair_repack(
77
+ source_path,
78
+ output_path,
79
+ overwrite=overwrite,
80
+ max_entry_size=max_entry_size,
81
+ max_total_size=max_total_size,
82
+ )
83
+ validation = validate_package(output_path)
84
+ open_safety = validate_editor_open_safety(output_path)
85
+ return {
86
+ "outputPath": self._context._relative_path(output_path),
87
+ "entries": list(result.entries),
88
+ "entryCount": len(result.entries),
89
+ "reordered": result.reordered,
90
+ "crcOk": result.crc_ok,
91
+ "recovered": result.recovered,
92
+ "validatePackage": {
93
+ "ok": validation.ok,
94
+ "errors": [str(issue) for issue in validation.errors],
95
+ "warnings": [str(issue) for issue in validation.warnings],
96
+ },
97
+ "openSafety": open_safety.to_dict(),
98
+ }
99
+
100
+ def list_master_pages_histories_versions(self, path: str) -> Dict[str, Any]:
101
+ document, _ = self._context._open_document(path)
102
+ master_pages = [
103
+ getattr(page, "part_name", None) for page in document.master_pages
104
+ ]
105
+ histories = [
106
+ getattr(history, "part_name", None) for history in document.histories
107
+ ]
108
+ version = document.version
109
+ version_info = (
110
+ asdict(cast(Any, version)) if version and dataclasses.is_dataclass(version) else None
111
+ )
112
+ return {
113
+ "masterPages": master_pages,
114
+ "histories": histories,
115
+ "versions": version_info,
116
+ }
117
+
118
+ def validate_structure(self, path: str, level: str = "basic") -> Dict[str, Any]:
119
+ resolved = self._context._resolve_path(path)
120
+ report: ValidationReport = validate_document_path(resolved)
121
+ issues = [
122
+ {
123
+ "part": issue.part_name,
124
+ "message": issue.message,
125
+ }
126
+ for issue in report.issues
127
+ ]
128
+ return {"ok": not issues, "issues": issues}
129
+
130
+ def lint_text_conventions(
131
+ self,
132
+ path: str,
133
+ *,
134
+ max_line_len: Optional[int] = None,
135
+ forbid_patterns: Optional[Sequence[str]] = None,
136
+ ) -> Dict[str, Any]:
137
+ resolved = self._context._resolve_path(path)
138
+ patterns = [re.compile(pat) for pat in (forbid_patterns or [])]
139
+ warnings: List[Dict[str, Any]] = []
140
+ with create_text_extractor(resolved) as extractor:
141
+ for paragraph in extractor.iter_document_paragraphs():
142
+ text = paragraph.text()
143
+ if max_line_len is not None and len(text) > max_line_len:
144
+ warnings.append(
145
+ {
146
+ "paragraphIndex": paragraph.index,
147
+ "message": f"Paragraph exceeds {max_line_len} characters",
148
+ }
149
+ )
150
+ for pattern in patterns:
151
+ if pattern.search(text):
152
+ warnings.append(
153
+ {
154
+ "paragraphIndex": paragraph.index,
155
+ "message": f"Pattern '{pattern.pattern}' found",
156
+ }
157
+ )
158
+ return {"warnings": warnings}
159
+
160
+ def package_get_xml(self, path: str, part_name: str) -> Dict[str, Any]:
161
+ resolved = self._context._resolve_path(path)
162
+ package = open_package(resolved)
163
+ element = package.get_xml(part_name)
164
+
165
+ xml_string = ET.tostring(element, encoding="unicode")
166
+ return {"xmlString": xml_string}
@@ -0,0 +1,201 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import Any, Dict, List, Optional, Sequence
7
+ from uuid import uuid4
8
+
9
+ from ..core.plan import (
10
+ ApplyEditInput,
11
+ GetContextInput,
12
+ PipelineError,
13
+ PlanEditInput,
14
+ PlanManager,
15
+ PreviewEditInput,
16
+ SearchHitModel,
17
+ SearchInput,
18
+ SearchOutput,
19
+ )
20
+ from ..metadata import tools_meta
21
+ from ..upstream import (
22
+ create_text_extractor,
23
+ )
24
+
25
+ from .context import DocumentContext
26
+
27
+ logger = logging.getLogger("hwpx_automation.hwpx_ops")
28
+
29
+
30
+ class PlanningService:
31
+ def __init__(self, context: DocumentContext) -> None:
32
+ self._context = context
33
+ self._manager = PlanManager()
34
+
35
+ @property
36
+ def plan_manager(self) -> PlanManager:
37
+ return self._manager
38
+
39
+ def _ensure_planner_document(self, doc_id: str, path: str) -> None:
40
+ resolved = self._context._resolve_path(path)
41
+ paragraphs: List[str] = []
42
+ with create_text_extractor(resolved) as extractor:
43
+ for paragraph in extractor.iter_document_paragraphs():
44
+ paragraphs.append(paragraph.text(preserve_breaks=True))
45
+ self._manager.register_document(doc_id, "\n".join(paragraphs))
46
+
47
+ def plan_edit(
48
+ self,
49
+ *,
50
+ path: str,
51
+ operations: Sequence[Dict[str, Any]],
52
+ trace_id: Optional[str] = None,
53
+ ) -> Dict[str, Any]:
54
+ payload = PlanEditInput.model_validate(
55
+ {"path": path, "operations": operations, "traceId": trace_id}
56
+ )
57
+ doc_path = payload.path_or_none()
58
+ if doc_path is not None:
59
+ self._ensure_planner_document(payload.doc_id, doc_path)
60
+ trace = payload.trace_id or f"plan-{uuid4().hex}"
61
+ try:
62
+ record = self._manager.create_plan_record(
63
+ payload.doc_id, payload.operations, trace_id=trace
64
+ )
65
+ except PipelineError as error:
66
+ return self._manager.error_response(payload.doc_id, trace, error)
67
+ return self._manager.plan_response(record)
68
+
69
+ def preview_edit(
70
+ self,
71
+ *,
72
+ plan_id: str,
73
+ trace_id: Optional[str] = None,
74
+ ) -> Dict[str, Any]:
75
+ payload = PreviewEditInput.model_validate(
76
+ {"planId": plan_id, "traceId": trace_id}
77
+ )
78
+ trace = payload.trace_id or payload.plan_id
79
+ try:
80
+ preview = self._manager.preview_plan_record(payload.plan_id)
81
+ except PipelineError as error:
82
+ plan = self._manager.get_plan_record(payload.plan_id)
83
+ doc_id = plan.doc_id if plan is not None else payload.plan_id
84
+ return self._manager.error_response(
85
+ doc_id, trace, error, plan_id=payload.plan_id
86
+ )
87
+ return self._manager.preview_response(preview)
88
+
89
+ def apply_edit(
90
+ self,
91
+ *,
92
+ plan_id: str,
93
+ confirm: bool,
94
+ idempotency_key: Optional[str] = None,
95
+ trace_id: Optional[str] = None,
96
+ ) -> Dict[str, Any]:
97
+ payload = ApplyEditInput.model_validate(
98
+ {
99
+ "planId": plan_id,
100
+ "confirm": confirm,
101
+ "idempotencyKey": idempotency_key,
102
+ "traceId": trace_id,
103
+ }
104
+ )
105
+ trace = payload.trace_id or payload.plan_id
106
+ try:
107
+ result = self._manager.apply_plan_record(
108
+ payload.plan_id,
109
+ confirm=payload.confirm,
110
+ idempotency_key=payload.idempotency_key,
111
+ )
112
+ except PipelineError as error:
113
+ plan = self._manager.get_plan_record(payload.plan_id)
114
+ doc_id = plan.doc_id if plan is not None else payload.plan_id
115
+ template = (
116
+ tools_meta.ERROR_PREVIEW_REQUIRED
117
+ if error.error_code == "PREVIEW_REQUIRED"
118
+ else None
119
+ )
120
+ return self._manager.error_response(
121
+ doc_id,
122
+ trace,
123
+ error,
124
+ plan_id=payload.plan_id,
125
+ next_action=template,
126
+ )
127
+ plan_record = self._manager.get_plan_record(payload.plan_id)
128
+ if plan_record is None: # pragma: no cover - defensive
129
+ raise self._context._new_error(
130
+ "PLAN_RECORD_MISSING",
131
+ "plan record missing after apply",
132
+ details={"planId": payload.plan_id},
133
+ )
134
+ return self._manager.apply_response(plan_record, result, trace)
135
+
136
+ def search(
137
+ self,
138
+ *,
139
+ path: str,
140
+ pattern: str,
141
+ scope: Optional[str] = None,
142
+ is_regex: bool = False,
143
+ limit: int = 20,
144
+ ) -> Dict[str, Any]:
145
+ payload = SearchInput.model_validate(
146
+ {
147
+ "path": path,
148
+ "pattern": pattern,
149
+ "scope": scope,
150
+ "is_regex": is_regex,
151
+ "limit": limit,
152
+ }
153
+ )
154
+ doc_path = payload.path_or_none()
155
+ if doc_path is not None:
156
+ self._ensure_planner_document(payload.doc_id, doc_path)
157
+ try:
158
+ hits = self._manager.search_document(payload.doc_id, payload)
159
+ except PipelineError as error:
160
+ raise self._context._new_error(
161
+ "PIPELINE_ERROR",
162
+ error.message,
163
+ details={"pipelineCode": error.error_code},
164
+ hint=error.hint,
165
+ ) from error
166
+ models = [
167
+ SearchHitModel(
168
+ nodeId=hit.node_id,
169
+ paragraphIndex=hit.paragraph_index,
170
+ match=hit.match,
171
+ context=hit.context,
172
+ )
173
+ for hit in hits
174
+ ]
175
+ return SearchOutput(matches=models).model_dump(by_alias=True)
176
+
177
+ def get_context(
178
+ self,
179
+ *,
180
+ path: str,
181
+ target: Dict[str, Any],
182
+ window: int = 1,
183
+ ) -> Dict[str, Any]:
184
+ payload = GetContextInput.model_validate(
185
+ {"path": path, "target": target, "window": window}
186
+ )
187
+ doc_path = payload.path_or_none()
188
+ if doc_path is not None:
189
+ self._ensure_planner_document(payload.doc_id, doc_path)
190
+ try:
191
+ view = self._manager.context_window(
192
+ payload.doc_id, payload.target, window=payload.window
193
+ )
194
+ except PipelineError as error:
195
+ raise self._context._new_error(
196
+ "PIPELINE_ERROR",
197
+ error.message,
198
+ details={"pipelineCode": error.error_code},
199
+ hint=error.hint,
200
+ ) from error
201
+ return view.model_dump(by_alias=True)