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.
- hwpx_automation/__init__.py +61 -0
- hwpx_automation/__init__.pyi +27 -0
- hwpx_automation/__main__.py +8 -0
- hwpx_automation/agent_document.py +392 -0
- hwpx_automation/api.py +136 -0
- hwpx_automation/blind_eval.py +407 -0
- hwpx_automation/capabilities.py +110 -0
- hwpx_automation/compat.py +48 -0
- hwpx_automation/configuration.py +60 -0
- hwpx_automation/core/__init__.py +2 -0
- hwpx_automation/core/content.py +762 -0
- hwpx_automation/core/context.py +111 -0
- hwpx_automation/core/diff.py +53 -0
- hwpx_automation/core/document.py +37 -0
- hwpx_automation/core/formatting.py +513 -0
- hwpx_automation/core/handles.py +24 -0
- hwpx_automation/core/locations.py +205 -0
- hwpx_automation/core/locator.py +162 -0
- hwpx_automation/core/plan.py +680 -0
- hwpx_automation/core/resources.py +42 -0
- hwpx_automation/core/search.py +296 -0
- hwpx_automation/core/transactions.py +434 -0
- hwpx_automation/core/txn.py +48 -0
- hwpx_automation/document_state.py +95 -0
- hwpx_automation/errors.py +174 -0
- hwpx_automation/execution_lock.py +15 -0
- hwpx_automation/fastmcp_adapter.py +672 -0
- hwpx_automation/form_fill.py +1177 -0
- hwpx_automation/form_output_models.py +223 -0
- hwpx_automation/handlers/__init__.py +2 -0
- hwpx_automation/handlers/_shared.py +377 -0
- hwpx_automation/handlers/agent_document.py +257 -0
- hwpx_automation/handlers/authoring.py +750 -0
- hwpx_automation/handlers/content_edit.py +1078 -0
- hwpx_automation/handlers/form_fill.py +607 -0
- hwpx_automation/handlers/layout_style.py +660 -0
- hwpx_automation/handlers/quality_render.py +566 -0
- hwpx_automation/handlers/read_export.py +1295 -0
- hwpx_automation/handlers/specialized.py +624 -0
- hwpx_automation/handlers/tracked_changes.py +589 -0
- hwpx_automation/handlers/workflow.py +105 -0
- hwpx_automation/hwp_converter.py +227 -0
- hwpx_automation/hwp_support.py +94 -0
- hwpx_automation/hwpx_ops.py +1439 -0
- hwpx_automation/identity.json +263 -0
- hwpx_automation/identity.py +18 -0
- hwpx_automation/ingest_adapters.py +85 -0
- hwpx_automation/markdown_plan.py +216 -0
- hwpx_automation/mcp_cli.py +29 -0
- hwpx_automation/metadata/tools_meta.py +40 -0
- hwpx_automation/mixed_form.py +3007 -0
- hwpx_automation/mutation_models.py +401 -0
- hwpx_automation/network_policy.py +232 -0
- hwpx_automation/office/__init__.py +14 -0
- hwpx_automation/office/agent/__init__.py +125 -0
- hwpx_automation/office/agent/_batch_verification.py +383 -0
- hwpx_automation/office/agent/blueprint/__init__.py +58 -0
- hwpx_automation/office/agent/blueprint/bundle.py +282 -0
- hwpx_automation/office/agent/blueprint/catalog.py +136 -0
- hwpx_automation/office/agent/blueprint/dump.py +520 -0
- hwpx_automation/office/agent/blueprint/mapping.py +312 -0
- hwpx_automation/office/agent/blueprint/model.py +722 -0
- hwpx_automation/office/agent/blueprint/native.py +621 -0
- hwpx_automation/office/agent/blueprint/replay.py +622 -0
- hwpx_automation/office/agent/catalog.py +252 -0
- hwpx_automation/office/agent/cli.py +647 -0
- hwpx_automation/office/agent/commands.py +1383 -0
- hwpx_automation/office/agent/document.py +801 -0
- hwpx_automation/office/agent/form_plan.py +1760 -0
- hwpx_automation/office/agent/model.py +808 -0
- hwpx_automation/office/agent/path.py +155 -0
- hwpx_automation/office/agent/query.py +230 -0
- hwpx_automation/office/agent/story.py +207 -0
- hwpx_automation/office/authoring/__init__.py +3542 -0
- hwpx_automation/office/authoring/advanced_generators.py +154 -0
- hwpx_automation/office/authoring/builder/__init__.py +52 -0
- hwpx_automation/office/authoring/builder/core.py +996 -0
- hwpx_automation/office/authoring/builder/report.py +195 -0
- hwpx_automation/office/authoring/design/__init__.py +30 -0
- hwpx_automation/office/authoring/design/_support.py +144 -0
- hwpx_automation/office/authoring/design/composer.py +282 -0
- hwpx_automation/office/authoring/design/harvest.py +305 -0
- hwpx_automation/office/authoring/design/plan.py +69 -0
- hwpx_automation/office/authoring/design/profile.py +88 -0
- hwpx_automation/office/authoring/design/profiles/application_form/fragments/body.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/application_form/fragments/heading.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/application_form/fragments/info_table.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/application_form/fragments/title.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/application_form/profile.json +25 -0
- hwpx_automation/office/authoring/design/profiles/application_form/template.hwpx +0 -0
- hwpx_automation/office/authoring/design/profiles/home_notice/fragments/body.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/home_notice/fragments/heading.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/home_notice/fragments/title.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/home_notice/profile.json +24 -0
- hwpx_automation/office/authoring/design/profiles/home_notice/template.hwpx +0 -0
- hwpx_automation/office/authoring/design/profiles/official_notice/fragments/body.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/official_notice/fragments/heading.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/official_notice/fragments/info_table.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/official_notice/fragments/title.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/official_notice/profile.json +25 -0
- hwpx_automation/office/authoring/design/profiles/official_notice/template.hwpx +0 -0
- hwpx_automation/office/authoring/design/profiles/report/fragments/body.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/report/fragments/heading.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/report/fragments/info_table.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/report/fragments/title.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/report/profile.json +25 -0
- hwpx_automation/office/authoring/design/profiles/report/template.hwpx +0 -0
- hwpx_automation/office/authoring/design/validator.py +107 -0
- hwpx_automation/office/authoring/presets/__init__.py +22 -0
- hwpx_automation/office/authoring/presets/proposal.py +538 -0
- hwpx_automation/office/authoring/report_parser.py +141 -0
- hwpx_automation/office/authoring/style_profile.py +437 -0
- hwpx_automation/office/authoring/template_analyzer.py +657 -0
- hwpx_automation/office/compliance/__init__.py +38 -0
- hwpx_automation/office/compliance/official_lint.py +478 -0
- hwpx_automation/office/compliance/pii.py +388 -0
- hwpx_automation/office/document_ops/__init__.py +13 -0
- hwpx_automation/office/document_ops/comparison.py +62 -0
- hwpx_automation/office/document_ops/mail_merge.py +73 -0
- hwpx_automation/office/document_ops/redline.py +35 -0
- hwpx_automation/office/evalplan/__init__.py +36 -0
- hwpx_automation/office/evalplan/runtime.py +2762 -0
- hwpx_automation/office/exam/__init__.py +44 -0
- hwpx_automation/office/exam/compose.py +282 -0
- hwpx_automation/office/exam/ir.py +44 -0
- hwpx_automation/office/exam/measure.py +163 -0
- hwpx_automation/office/exam/parser.py +150 -0
- hwpx_automation/office/exam/profile.py +123 -0
- hwpx_automation/office/form_fill/__init__.py +66 -0
- hwpx_automation/office/form_fill/classification.py +108 -0
- hwpx_automation/office/form_fill/fill_residue.py +242 -0
- hwpx_automation/office/form_fill/fit/__init__.py +36 -0
- hwpx_automation/office/form_fill/fit/apply.py +24 -0
- hwpx_automation/office/form_fill/fit/engine.py +24 -0
- hwpx_automation/office/form_fill/fit/measure.py +50 -0
- hwpx_automation/office/form_fill/fit/policy.py +28 -0
- hwpx_automation/office/form_fill/fit/report.py +28 -0
- hwpx_automation/office/form_fill/fit/seal.py +457 -0
- hwpx_automation/office/form_fill/fit/wordbox.py +1343 -0
- hwpx_automation/office/form_fill/guidance.py +704 -0
- hwpx_automation/office/form_fill/quality.py +961 -0
- hwpx_automation/office/form_fill/split_run.py +333 -0
- hwpx_automation/office/form_fill/template_formfit.py +656 -0
- hwpx_automation/office/house_style/__init__.py +196 -0
- hwpx_automation/office/house_style/composition.py +68 -0
- hwpx_automation/office/house_style/data/bank.json +625 -0
- hwpx_automation/office/house_style/data/genres.json +43 -0
- hwpx_automation/office/quality/__init__.py +14 -0
- hwpx_automation/office/quality/page_guard.py +277 -0
- hwpx_automation/office/rendering/__init__.py +145 -0
- hwpx_automation/office/rendering/_hancom_open_rate.ps1 +374 -0
- hwpx_automation/office/rendering/_refresh_hwpx_mac.applescript +162 -0
- hwpx_automation/office/rendering/_render_hwpx.ps1 +72 -0
- hwpx_automation/office/rendering/_render_hwpx_mac.applescript +249 -0
- hwpx_automation/office/rendering/block_splits.py +76 -0
- hwpx_automation/office/rendering/detectors.py +151 -0
- hwpx_automation/office/rendering/diff.py +153 -0
- hwpx_automation/office/rendering/fixture_corpus.py +215 -0
- hwpx_automation/office/rendering/oracle.py +909 -0
- hwpx_automation/office/rendering/page_qa.py +245 -0
- hwpx_automation/office/rendering/qa_contracts.py +293 -0
- hwpx_automation/office/rendering/qa_metrics.py +241 -0
- hwpx_automation/office/rendering/worker.py +290 -0
- hwpx_automation/office/utilities/__init__.py +12 -0
- hwpx_automation/office/utilities/table_compute.py +477 -0
- hwpx_automation/ops_services/__init__.py +1 -0
- hwpx_automation/ops_services/_border_fill.py +283 -0
- hwpx_automation/ops_services/composition.py +55 -0
- hwpx_automation/ops_services/content_layout.py +322 -0
- hwpx_automation/ops_services/context.py +213 -0
- hwpx_automation/ops_services/form_fields.py +557 -0
- hwpx_automation/ops_services/media.py +178 -0
- hwpx_automation/ops_services/memo_style.py +477 -0
- hwpx_automation/ops_services/package_validation.py +166 -0
- hwpx_automation/ops_services/planning.py +201 -0
- hwpx_automation/ops_services/preview_export.py +585 -0
- hwpx_automation/ops_services/read_query.py +601 -0
- hwpx_automation/ops_services/save_policy.py +604 -0
- hwpx_automation/ops_services/tables.py +539 -0
- hwpx_automation/ops_services/transactions.py +616 -0
- hwpx_automation/preview_output_models.py +69 -0
- hwpx_automation/public-modules.json +206 -0
- hwpx_automation/py.typed +1 -0
- hwpx_automation/quality.py +351 -0
- hwpx_automation/quality_generation.py +725 -0
- hwpx_automation/runtime.py +321 -0
- hwpx_automation/runtime_services.py +100 -0
- hwpx_automation/server.py +259 -0
- hwpx_automation/storage.py +747 -0
- hwpx_automation/tool_bindings.py +170 -0
- hwpx_automation/tool_contract.py +982 -0
- hwpx_automation/upstream.py +755 -0
- hwpx_automation/utils/__init__.py +2 -0
- hwpx_automation/utils/helpers.py +29 -0
- hwpx_automation/visual_qa.py +667 -0
- hwpx_automation/workflow/__init__.py +55 -0
- hwpx_automation/workflow/adapters.py +482 -0
- hwpx_automation/workflow/dispatcher.py +213 -0
- hwpx_automation/workflow/models.py +243 -0
- hwpx_automation/workflow/policy.py +198 -0
- hwpx_automation/workflow/render_contracts.py +173 -0
- hwpx_automation/workflow/render_metrics.py +196 -0
- hwpx_automation/workflow/render_queue.py +482 -0
- hwpx_automation/workflow/render_security.py +172 -0
- hwpx_automation/workflow/render_transport.py +369 -0
- hwpx_automation/workflow/rendering.py +206 -0
- hwpx_automation/workflow/service.py +758 -0
- hwpx_automation/workflow/state_machine.py +65 -0
- hwpx_automation/workflow/store.py +747 -0
- hwpx_automation/workspace.py +1694 -0
- python_hwpx_automation-6.0.3.dist-info/METADATA +279 -0
- python_hwpx_automation-6.0.3.dist-info/RECORD +217 -0
- python_hwpx_automation-6.0.3.dist-info/WHEEL +5 -0
- python_hwpx_automation-6.0.3.dist-info/entry_points.txt +3 -0
- python_hwpx_automation-6.0.3.dist-info/licenses/LICENSE +178 -0
- python_hwpx_automation-6.0.3.dist-info/licenses/NOTICE +14 -0
- python_hwpx_automation-6.0.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Stable Python facade for HWPX task automation.
|
|
3
|
+
|
|
4
|
+
The base distribution deliberately does not import the optional MCP SDK. MCP
|
|
5
|
+
transport users install ``python-hwpx-automation[mcp]`` and start
|
|
6
|
+
``hwpx-automation-mcp``; Python callers can use the curated task facade below.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from importlib import import_module
|
|
10
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
_CANONICAL_DISTRIBUTION = "python-hwpx-automation"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _resolve_version() -> str:
|
|
17
|
+
try:
|
|
18
|
+
return version(_CANONICAL_DISTRIBUTION)
|
|
19
|
+
except PackageNotFoundError:
|
|
20
|
+
return "0+unknown" # 체크아웃에서 직접 실행하는 개발 경로
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
__version__ = _resolve_version()
|
|
24
|
+
|
|
25
|
+
_API_EXPORTS = {
|
|
26
|
+
"CanonicalFormFillInput",
|
|
27
|
+
"ComposeResult",
|
|
28
|
+
"DocumentPlan",
|
|
29
|
+
"DocumentStylePreset",
|
|
30
|
+
"EvalPlanContent",
|
|
31
|
+
"FormFillAnalyzeOptions",
|
|
32
|
+
"FormFillPlanInput",
|
|
33
|
+
"Rubric",
|
|
34
|
+
"analyze_form_fill",
|
|
35
|
+
"apply_form_fill",
|
|
36
|
+
"compose_exam",
|
|
37
|
+
"create_document_from_plan",
|
|
38
|
+
"fill_evalplan",
|
|
39
|
+
"finalize_evalplan",
|
|
40
|
+
"normalize_document_plan",
|
|
41
|
+
"parse_evalplan_review",
|
|
42
|
+
"parse_exam_markdown",
|
|
43
|
+
"validate_document_plan",
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def __getattr__(name: str) -> Any:
|
|
48
|
+
"""Load the task facade on first use without creating package cycles."""
|
|
49
|
+
|
|
50
|
+
if name not in _API_EXPORTS:
|
|
51
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
52
|
+
value = getattr(import_module(f"{__name__}.api"), name)
|
|
53
|
+
globals()[name] = value
|
|
54
|
+
return value
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def __dir__() -> list[str]:
|
|
58
|
+
return sorted(set(globals()) | _API_EXPORTS)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
__all__ = ["__version__", *sorted(_API_EXPORTS)]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Typed surface for the lazy :mod:`hwpx_automation` package facade."""
|
|
2
|
+
|
|
3
|
+
from .api import (
|
|
4
|
+
CanonicalFormFillInput as CanonicalFormFillInput,
|
|
5
|
+
ComposeResult as ComposeResult,
|
|
6
|
+
DocumentPlan as DocumentPlan,
|
|
7
|
+
DocumentStylePreset as DocumentStylePreset,
|
|
8
|
+
EvalPlanContent as EvalPlanContent,
|
|
9
|
+
FormFillAnalyzeOptions as FormFillAnalyzeOptions,
|
|
10
|
+
FormFillPlanInput as FormFillPlanInput,
|
|
11
|
+
Rubric as Rubric,
|
|
12
|
+
analyze_form_fill as analyze_form_fill,
|
|
13
|
+
apply_form_fill as apply_form_fill,
|
|
14
|
+
compose_exam as compose_exam,
|
|
15
|
+
create_document_from_plan as create_document_from_plan,
|
|
16
|
+
fill_evalplan as fill_evalplan,
|
|
17
|
+
finalize_evalplan as finalize_evalplan,
|
|
18
|
+
normalize_document_plan as normalize_document_plan,
|
|
19
|
+
parse_evalplan_review as parse_evalplan_review,
|
|
20
|
+
parse_exam_markdown as parse_exam_markdown,
|
|
21
|
+
validate_document_plan as validate_document_plan,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
__version__: str
|
|
25
|
+
__all__: list[str]
|
|
26
|
+
|
|
27
|
+
def __dir__() -> list[str]: ...
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Thin MCP transport adapter for the canonical automation agent runtime.
|
|
3
|
+
|
|
4
|
+
This module deliberately owns no path grammar, selector parser, document
|
|
5
|
+
projection, or mutation compiler. Those semantics live in
|
|
6
|
+
``hwpx_automation.office.agent``; the frozen core 4.x compatibility copy is
|
|
7
|
+
verified against it by the parity corpus.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import hashlib
|
|
13
|
+
from collections.abc import Mapping, MutableMapping
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Annotated, Any, Iterator, Literal
|
|
16
|
+
|
|
17
|
+
from pydantic import Field
|
|
18
|
+
|
|
19
|
+
from hwpx_automation.office.agent import (
|
|
20
|
+
AGENT_BATCH_SCHEMA,
|
|
21
|
+
AgentBatchResult,
|
|
22
|
+
AgentContractError,
|
|
23
|
+
AgentError,
|
|
24
|
+
HwpxAgentDocument,
|
|
25
|
+
agent_catalog,
|
|
26
|
+
agent_json_schemas,
|
|
27
|
+
apply_document_commands as apply_core_document_commands,
|
|
28
|
+
catalog_hash,
|
|
29
|
+
human_help,
|
|
30
|
+
)
|
|
31
|
+
from hwpx_automation.office.agent.blueprint import (
|
|
32
|
+
BLUEPRINT_REPLAY_RESULT_SCHEMA,
|
|
33
|
+
BlueprintReplayResult,
|
|
34
|
+
blueprint_catalog,
|
|
35
|
+
blueprint_catalog_hash,
|
|
36
|
+
blueprint_human_help,
|
|
37
|
+
blueprint_json_schemas,
|
|
38
|
+
dump_document_blueprint as dump_core_document_blueprint,
|
|
39
|
+
replay_document_blueprint as replay_core_document_blueprint,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
_EMPTY_REVISION = "sha256:" + hashlib.sha256(b"").hexdigest()
|
|
43
|
+
_DEFAULT_REQUIREMENTS = ("package", "reopen", "openSafety", "semanticDiff", "bytePreservation")
|
|
44
|
+
_SCHEMAS = agent_json_schemas()
|
|
45
|
+
_CATALOG = agent_catalog()
|
|
46
|
+
_BLUEPRINT_SCHEMAS = blueprint_json_schemas()
|
|
47
|
+
_BLUEPRINT_CATALOG = blueprint_catalog()
|
|
48
|
+
|
|
49
|
+
# FastMCP builds its input JSON Schema from the public function annotation.
|
|
50
|
+
# Supplying the core-generated command union here prevents a second handwritten
|
|
51
|
+
# MCP command schema from becoming an independent source of truth.
|
|
52
|
+
AgentCommandList = Annotated[
|
|
53
|
+
list[dict[str, Any]],
|
|
54
|
+
Field(
|
|
55
|
+
min_length=1,
|
|
56
|
+
max_length=int(_CATALOG["limits"]["maxCommands"]),
|
|
57
|
+
json_schema_extra={"items": _SCHEMAS["command"]},
|
|
58
|
+
),
|
|
59
|
+
]
|
|
60
|
+
AgentViewDepth = Annotated[
|
|
61
|
+
int,
|
|
62
|
+
Field(ge=0, le=int(_CATALOG["limits"]["maxViewDepth"])),
|
|
63
|
+
]
|
|
64
|
+
AgentChildLimit = Annotated[
|
|
65
|
+
int,
|
|
66
|
+
Field(ge=1, le=int(_CATALOG["limits"]["maxChildrenPerNode"])),
|
|
67
|
+
]
|
|
68
|
+
AgentQueryLimit = Annotated[
|
|
69
|
+
int,
|
|
70
|
+
Field(ge=1, le=int(_CATALOG["limits"]["maxQueryResults"])),
|
|
71
|
+
]
|
|
72
|
+
AgentSelector = Annotated[
|
|
73
|
+
str,
|
|
74
|
+
Field(min_length=1, max_length=int(_CATALOG["limits"]["maxSelectorChars"])),
|
|
75
|
+
]
|
|
76
|
+
AgentRevision = Annotated[
|
|
77
|
+
str | None,
|
|
78
|
+
Field(pattern=r"^sha256:[a-f0-9]{64}$"),
|
|
79
|
+
]
|
|
80
|
+
BlueprintPath = Annotated[
|
|
81
|
+
str,
|
|
82
|
+
Field(min_length=1, max_length=4096),
|
|
83
|
+
]
|
|
84
|
+
BlueprintReplayRequest = Annotated[
|
|
85
|
+
dict[str, Any],
|
|
86
|
+
Field(json_schema_extra=_BLUEPRINT_SCHEMAS["replay"]),
|
|
87
|
+
]
|
|
88
|
+
BlueprintMode = Literal["portable", "source-bound"]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _suggestion(code: str) -> str:
|
|
92
|
+
suggestions = {
|
|
93
|
+
"not_found": "Check the document path, then retry.",
|
|
94
|
+
"stale_revision": "Read the document again and retry with its current revision.",
|
|
95
|
+
"ambiguous_target": "Query again and choose one unique canonical path.",
|
|
96
|
+
"volatile_target": "Refresh the positional path from the current document revision.",
|
|
97
|
+
"verification_failed": "Do not publish the output; inspect the verification evidence.",
|
|
98
|
+
}
|
|
99
|
+
return suggestions.get(code, "Inspect the target and the shared agent catalog before retrying.")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _agent_error(exc: BaseException, *, target: str) -> AgentError:
|
|
103
|
+
if isinstance(exc, AgentContractError):
|
|
104
|
+
code = exc.code
|
|
105
|
+
message = str(exc)
|
|
106
|
+
resolved_target = exc.target or target
|
|
107
|
+
elif isinstance(exc, FileNotFoundError):
|
|
108
|
+
code = "not_found"
|
|
109
|
+
message = str(exc)
|
|
110
|
+
resolved_target = target
|
|
111
|
+
elif isinstance(exc, (PermissionError, OSError)):
|
|
112
|
+
code = "verification_failed"
|
|
113
|
+
message = f"{type(exc).__name__}: {exc}"
|
|
114
|
+
resolved_target = target
|
|
115
|
+
else:
|
|
116
|
+
code = "verification_failed"
|
|
117
|
+
message = f"{type(exc).__name__}: {exc}"
|
|
118
|
+
resolved_target = target
|
|
119
|
+
recoverability = "retryable" if code == "stale_revision" else "terminal"
|
|
120
|
+
if code in {"not_found", "ambiguous_target", "volatile_target", "unsupported_content"}:
|
|
121
|
+
recoverability = "needs-review"
|
|
122
|
+
return AgentError(
|
|
123
|
+
code=code,
|
|
124
|
+
message=message[:4096],
|
|
125
|
+
target=resolved_target,
|
|
126
|
+
recoverability=recoverability,
|
|
127
|
+
suggestion=_suggestion(code),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def error_payload(exc: BaseException, *, target: str) -> dict[str, Any]:
|
|
132
|
+
"""Return the shared structured error shape for read-side failures."""
|
|
133
|
+
|
|
134
|
+
return {"ok": False, "error": _agent_error(exc, target=target).to_dict()}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def batch_error_payload(
|
|
138
|
+
exc: BaseException,
|
|
139
|
+
*,
|
|
140
|
+
input_filename: str,
|
|
141
|
+
output_filename: str,
|
|
142
|
+
dry_run: bool,
|
|
143
|
+
) -> dict[str, Any]:
|
|
144
|
+
"""Return a valid batch-result failure when the MCP boundary blocks a write."""
|
|
145
|
+
|
|
146
|
+
revision = _EMPTY_REVISION
|
|
147
|
+
try:
|
|
148
|
+
data = Path(input_filename).read_bytes()
|
|
149
|
+
except OSError:
|
|
150
|
+
pass
|
|
151
|
+
else:
|
|
152
|
+
revision = "sha256:" + hashlib.sha256(data).hexdigest()
|
|
153
|
+
return AgentBatchResult(
|
|
154
|
+
ok=False,
|
|
155
|
+
rolled_back=True,
|
|
156
|
+
dry_run=dry_run,
|
|
157
|
+
input_revision=revision,
|
|
158
|
+
document_revision=revision,
|
|
159
|
+
output_filename=output_filename,
|
|
160
|
+
verification_report={
|
|
161
|
+
"schemaVersion": AGENT_BATCH_SCHEMA,
|
|
162
|
+
"catalogHash": catalog_hash(),
|
|
163
|
+
"boundary": "mcp-capability-and-locator",
|
|
164
|
+
},
|
|
165
|
+
error=_agent_error(exc, target="batch"),
|
|
166
|
+
).to_dict()
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class ScopedIdempotencyStore(MutableMapping[str, Any]):
|
|
170
|
+
"""Namespace a core caller-owned store inside the server's bounded cache."""
|
|
171
|
+
|
|
172
|
+
def __init__(
|
|
173
|
+
self,
|
|
174
|
+
backing: MutableMapping[str, Any],
|
|
175
|
+
namespace: str,
|
|
176
|
+
*,
|
|
177
|
+
family: str = "agent-document",
|
|
178
|
+
) -> None:
|
|
179
|
+
self._backing = backing
|
|
180
|
+
self._prefix = f"{family}:{namespace}:"
|
|
181
|
+
|
|
182
|
+
def _key(self, key: str) -> str:
|
|
183
|
+
return self._prefix + key
|
|
184
|
+
|
|
185
|
+
def __getitem__(self, key: str) -> Any:
|
|
186
|
+
return self._backing[self._key(key)]
|
|
187
|
+
|
|
188
|
+
def __setitem__(self, key: str, value: Any) -> None:
|
|
189
|
+
self._backing[self._key(key)] = value
|
|
190
|
+
|
|
191
|
+
def __delitem__(self, key: str) -> None:
|
|
192
|
+
del self._backing[self._key(key)]
|
|
193
|
+
|
|
194
|
+
def __iter__(self) -> Iterator[str]:
|
|
195
|
+
for key in self._backing:
|
|
196
|
+
if key.startswith(self._prefix):
|
|
197
|
+
yield key[len(self._prefix) :]
|
|
198
|
+
|
|
199
|
+
def __len__(self) -> int:
|
|
200
|
+
return sum(1 for _ in self)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def read_document_node(
|
|
204
|
+
filename: str,
|
|
205
|
+
*,
|
|
206
|
+
path: str,
|
|
207
|
+
depth: int,
|
|
208
|
+
child_limit: int,
|
|
209
|
+
expected_revision: str | None,
|
|
210
|
+
) -> dict[str, Any]:
|
|
211
|
+
with HwpxAgentDocument.open(filename) as document:
|
|
212
|
+
return document.get(
|
|
213
|
+
path,
|
|
214
|
+
depth=depth,
|
|
215
|
+
child_limit=child_limit,
|
|
216
|
+
expected_revision=expected_revision,
|
|
217
|
+
).to_dict()
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def query_document_node_records(
|
|
221
|
+
filename: str,
|
|
222
|
+
*,
|
|
223
|
+
selector: str,
|
|
224
|
+
limit: int,
|
|
225
|
+
node_depth: int,
|
|
226
|
+
child_limit: int,
|
|
227
|
+
expected_revision: str | None,
|
|
228
|
+
) -> dict[str, Any]:
|
|
229
|
+
with HwpxAgentDocument.open(filename) as document:
|
|
230
|
+
return document.query(
|
|
231
|
+
selector,
|
|
232
|
+
limit=limit,
|
|
233
|
+
node_depth=node_depth,
|
|
234
|
+
child_limit=child_limit,
|
|
235
|
+
expected_revision=expected_revision,
|
|
236
|
+
).to_dict()
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def apply_document_command_batch(
|
|
240
|
+
*,
|
|
241
|
+
filename: str,
|
|
242
|
+
output: str,
|
|
243
|
+
commands: list[Mapping[str, Any]],
|
|
244
|
+
expected_revision: str | None,
|
|
245
|
+
idempotency_key: str | None,
|
|
246
|
+
dry_run: bool,
|
|
247
|
+
quality: str | Mapping[str, Any] | None,
|
|
248
|
+
verification_requirements: list[str] | None,
|
|
249
|
+
overwrite: bool,
|
|
250
|
+
idempotency_store: MutableMapping[str, Any],
|
|
251
|
+
) -> dict[str, Any]:
|
|
252
|
+
batch = {
|
|
253
|
+
"schemaVersion": AGENT_BATCH_SCHEMA,
|
|
254
|
+
"input": {"filename": filename},
|
|
255
|
+
"output": {"filename": output, "overwrite": overwrite},
|
|
256
|
+
"commands": [dict(command) for command in commands],
|
|
257
|
+
"expectedRevision": expected_revision,
|
|
258
|
+
"idempotencyKey": idempotency_key,
|
|
259
|
+
"dryRun": dry_run,
|
|
260
|
+
"quality": quality,
|
|
261
|
+
"verificationRequirements": list(
|
|
262
|
+
verification_requirements or _DEFAULT_REQUIREMENTS
|
|
263
|
+
),
|
|
264
|
+
}
|
|
265
|
+
return apply_core_document_commands(
|
|
266
|
+
batch,
|
|
267
|
+
idempotency_store=idempotency_store,
|
|
268
|
+
).to_dict()
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def dump_blueprint_document(
|
|
272
|
+
*,
|
|
273
|
+
filename: str,
|
|
274
|
+
path: str,
|
|
275
|
+
mode: str,
|
|
276
|
+
expected_revision: str | None,
|
|
277
|
+
output: str | None,
|
|
278
|
+
overwrite: bool,
|
|
279
|
+
include_assets: bool,
|
|
280
|
+
require_replayable: bool,
|
|
281
|
+
include_manifest: bool,
|
|
282
|
+
) -> dict[str, Any]:
|
|
283
|
+
result = dump_core_document_blueprint(
|
|
284
|
+
filename,
|
|
285
|
+
path=path,
|
|
286
|
+
mode=mode,
|
|
287
|
+
expected_revision=expected_revision,
|
|
288
|
+
output=output,
|
|
289
|
+
overwrite=overwrite,
|
|
290
|
+
include_assets=include_assets,
|
|
291
|
+
require_replayable=require_replayable,
|
|
292
|
+
)
|
|
293
|
+
return result.to_dict(include_manifest=include_manifest)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def replay_blueprint_document(
|
|
297
|
+
request: Mapping[str, Any],
|
|
298
|
+
*,
|
|
299
|
+
idempotency_store: MutableMapping[str, Any],
|
|
300
|
+
) -> dict[str, Any]:
|
|
301
|
+
return replay_core_document_blueprint(
|
|
302
|
+
dict(request),
|
|
303
|
+
idempotency_store=idempotency_store,
|
|
304
|
+
).to_dict()
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def blueprint_replay_error_payload(
|
|
308
|
+
exc: BaseException,
|
|
309
|
+
*,
|
|
310
|
+
request: Mapping[str, Any],
|
|
311
|
+
) -> dict[str, Any]:
|
|
312
|
+
target = request.get("target") if isinstance(request.get("target"), Mapping) else {}
|
|
313
|
+
bundle = request.get("bundle") if isinstance(request.get("bundle"), Mapping) else {}
|
|
314
|
+
input_filename = str(target.get("input") or "")
|
|
315
|
+
output_filename = str(target.get("output") or "")
|
|
316
|
+
revision = _EMPTY_REVISION
|
|
317
|
+
try:
|
|
318
|
+
revision = "sha256:" + hashlib.sha256(Path(input_filename).read_bytes()).hexdigest()
|
|
319
|
+
except OSError:
|
|
320
|
+
pass
|
|
321
|
+
blueprint_hash = str(bundle.get("blueprintHash") or "")
|
|
322
|
+
if not blueprint_hash.startswith("sha256:") or len(blueprint_hash) != 71:
|
|
323
|
+
blueprint_hash = "sha256:" + "0" * 64
|
|
324
|
+
return BlueprintReplayResult(
|
|
325
|
+
ok=False,
|
|
326
|
+
rolled_back=True,
|
|
327
|
+
dry_run=bool(request.get("dryRun", False)),
|
|
328
|
+
input_revision=revision,
|
|
329
|
+
document_revision=revision,
|
|
330
|
+
output_filename=output_filename,
|
|
331
|
+
blueprint_hash=blueprint_hash,
|
|
332
|
+
verification_report={
|
|
333
|
+
"schemaVersion": BLUEPRINT_REPLAY_RESULT_SCHEMA,
|
|
334
|
+
"catalogHash": blueprint_catalog_hash(),
|
|
335
|
+
"boundary": "mcp-capability-and-locator",
|
|
336
|
+
},
|
|
337
|
+
error=_agent_error(exc, target="replay"),
|
|
338
|
+
).to_dict()
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def tool_help() -> dict[str, str]:
|
|
342
|
+
"""Generate MCP descriptions from the same catalog as ``hwpx help``."""
|
|
343
|
+
|
|
344
|
+
selector_examples = ", ".join(_CATALOG["query"]["examples"])
|
|
345
|
+
return {
|
|
346
|
+
"get": (
|
|
347
|
+
"Get one bounded HWPX semantic node by canonical path. "
|
|
348
|
+
f"Shared catalog {catalog_hash()}.\n\n{human_help()}"
|
|
349
|
+
),
|
|
350
|
+
"query": (
|
|
351
|
+
"Query bounded semantic nodes with selector v1 and return canonical paths. "
|
|
352
|
+
f"Examples: {selector_examples}. Shared catalog {catalog_hash()}."
|
|
353
|
+
),
|
|
354
|
+
"apply": (
|
|
355
|
+
"Apply one heterogeneous atomic HWPX command batch using only the shared "
|
|
356
|
+
"set/add/remove/move/copy union; all commands commit once or roll back. "
|
|
357
|
+
f"Shared catalog {catalog_hash()}."
|
|
358
|
+
),
|
|
359
|
+
"dumpBlueprint": (
|
|
360
|
+
"Dump one revision-bound HWPX document/subtree into a validated typed .hwpxbp; "
|
|
361
|
+
"returns bounded manifest/fidelity evidence and never exposes raw XML. "
|
|
362
|
+
f"Shared blueprint catalog {blueprint_catalog_hash()}.\n\n{blueprint_human_help()}"
|
|
363
|
+
),
|
|
364
|
+
"replayBlueprint": (
|
|
365
|
+
"Atomically replay one validated typed blueprint request with strict dependency mapping, "
|
|
366
|
+
"caller-owned idempotency, one save, rollback, lossless/openSafety evidence, and no session state. "
|
|
367
|
+
f"Shared blueprint catalog {blueprint_catalog_hash()}."
|
|
368
|
+
),
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
__all__ = [
|
|
373
|
+
"AgentChildLimit",
|
|
374
|
+
"AgentCommandList",
|
|
375
|
+
"AgentQueryLimit",
|
|
376
|
+
"AgentRevision",
|
|
377
|
+
"AgentSelector",
|
|
378
|
+
"AgentViewDepth",
|
|
379
|
+
"BlueprintMode",
|
|
380
|
+
"BlueprintPath",
|
|
381
|
+
"BlueprintReplayRequest",
|
|
382
|
+
"ScopedIdempotencyStore",
|
|
383
|
+
"apply_document_command_batch",
|
|
384
|
+
"batch_error_payload",
|
|
385
|
+
"blueprint_replay_error_payload",
|
|
386
|
+
"dump_blueprint_document",
|
|
387
|
+
"error_payload",
|
|
388
|
+
"query_document_node_records",
|
|
389
|
+
"read_document_node",
|
|
390
|
+
"replay_blueprint_document",
|
|
391
|
+
"tool_help",
|
|
392
|
+
]
|
hwpx_automation/api.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Curated, MCP-independent task API for :mod:`hwpx_automation`.
|
|
3
|
+
|
|
4
|
+
Deep modules remain available for advanced users, but this facade is the
|
|
5
|
+
stable entry point promised by the ``python-hwpx-automation`` distribution.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any, Mapping
|
|
11
|
+
|
|
12
|
+
from .form_fill import (
|
|
13
|
+
CanonicalFormFillInput,
|
|
14
|
+
FormFillAnalyzeOptions,
|
|
15
|
+
FormFillPlanInput,
|
|
16
|
+
analyze_form_fill_workflow,
|
|
17
|
+
apply_form_fill_workflow,
|
|
18
|
+
)
|
|
19
|
+
from .office.authoring import (
|
|
20
|
+
DocumentPlan,
|
|
21
|
+
DocumentStylePreset,
|
|
22
|
+
create_document_from_plan,
|
|
23
|
+
normalize_document_plan,
|
|
24
|
+
validate_document_plan,
|
|
25
|
+
)
|
|
26
|
+
from .office.evalplan import (
|
|
27
|
+
EvalPlanContent,
|
|
28
|
+
Rubric,
|
|
29
|
+
fill_evalplan,
|
|
30
|
+
finalize_evalplan,
|
|
31
|
+
parse_review_file,
|
|
32
|
+
parse_review_md,
|
|
33
|
+
)
|
|
34
|
+
from .office.exam import ComposeResult, compose_exam_into_form, parse_exam_markdown
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def analyze_form_fill(
|
|
38
|
+
*,
|
|
39
|
+
source_filename: str,
|
|
40
|
+
input_json: CanonicalFormFillInput | dict[str, Any] | str | None = None,
|
|
41
|
+
input_json_path: str | None = None,
|
|
42
|
+
input_docx: str | None = None,
|
|
43
|
+
destination_filename: str | None = None,
|
|
44
|
+
options: FormFillAnalyzeOptions | dict[str, Any] | None = None,
|
|
45
|
+
) -> dict[str, Any]:
|
|
46
|
+
"""Analyze form fill without mutation and return a serializable plan.
|
|
47
|
+
|
|
48
|
+
Paths are resolved through the configured workspace boundary. Invalid or
|
|
49
|
+
ambiguous input raises the same typed/structured errors as the underlying
|
|
50
|
+
automation service; callers should not treat a partial plan as applied.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
return analyze_form_fill_workflow(
|
|
54
|
+
source_filename=source_filename,
|
|
55
|
+
input_json=input_json,
|
|
56
|
+
input_json_path=input_json_path,
|
|
57
|
+
input_docx=input_docx,
|
|
58
|
+
destination_filename=destination_filename,
|
|
59
|
+
options=options,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def apply_form_fill(
|
|
64
|
+
*,
|
|
65
|
+
plan_id: str | None = None,
|
|
66
|
+
analysis: FormFillPlanInput | dict[str, Any] | None = None,
|
|
67
|
+
source_filename: str | None = None,
|
|
68
|
+
destination_filename: str | None = None,
|
|
69
|
+
canonical_input: CanonicalFormFillInput | dict[str, Any] | str | None = None,
|
|
70
|
+
confirm: bool = True,
|
|
71
|
+
mask: bool = True,
|
|
72
|
+
) -> dict[str, Any]:
|
|
73
|
+
"""Apply a reviewed plan to a distinct destination and validate it.
|
|
74
|
+
|
|
75
|
+
Source-in-place writes and ``confirm=False`` fail before publication.
|
|
76
|
+
Unresolved mappings return a blocked handoff instead of a partial write.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
return apply_form_fill_workflow(
|
|
80
|
+
plan_id=plan_id,
|
|
81
|
+
analysis=analysis,
|
|
82
|
+
source_filename=source_filename,
|
|
83
|
+
destination_filename=destination_filename,
|
|
84
|
+
canonical_input=canonical_input,
|
|
85
|
+
confirm=confirm,
|
|
86
|
+
mask=mask,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def compose_exam(
|
|
91
|
+
form_path: str,
|
|
92
|
+
exam_markdown: str,
|
|
93
|
+
output_path: str,
|
|
94
|
+
*,
|
|
95
|
+
oracle: Any = None,
|
|
96
|
+
max_rounds: int = 2,
|
|
97
|
+
role_style_names: Mapping[str, str] | None = None,
|
|
98
|
+
) -> ComposeResult:
|
|
99
|
+
"""Compose exam Markdown into a destination form with honest render state."""
|
|
100
|
+
|
|
101
|
+
return compose_exam_into_form(
|
|
102
|
+
form_path,
|
|
103
|
+
exam_markdown,
|
|
104
|
+
output_path,
|
|
105
|
+
oracle=oracle,
|
|
106
|
+
max_rounds=max_rounds,
|
|
107
|
+
role_style_names=role_style_names,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def parse_evalplan_review(source: str, *, from_file: bool = False) -> EvalPlanContent:
|
|
112
|
+
"""Parse evaluation-plan review Markdown from text or a file path."""
|
|
113
|
+
|
|
114
|
+
return parse_review_file(source) if from_file else parse_review_md(source)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
__all__ = [
|
|
118
|
+
"CanonicalFormFillInput",
|
|
119
|
+
"ComposeResult",
|
|
120
|
+
"DocumentPlan",
|
|
121
|
+
"DocumentStylePreset",
|
|
122
|
+
"EvalPlanContent",
|
|
123
|
+
"FormFillAnalyzeOptions",
|
|
124
|
+
"FormFillPlanInput",
|
|
125
|
+
"Rubric",
|
|
126
|
+
"analyze_form_fill",
|
|
127
|
+
"apply_form_fill",
|
|
128
|
+
"compose_exam",
|
|
129
|
+
"create_document_from_plan",
|
|
130
|
+
"fill_evalplan",
|
|
131
|
+
"finalize_evalplan",
|
|
132
|
+
"normalize_document_plan",
|
|
133
|
+
"parse_evalplan_review",
|
|
134
|
+
"parse_exam_markdown",
|
|
135
|
+
"validate_document_plan",
|
|
136
|
+
]
|