graph-knowledge-doc-parser 0.2.2__py3-none-any.whl → 0.2.4__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: graph-knowledge-doc-parser
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: doc parser using llm driven engine with graph knowledge awareness
5
5
  Author: humblemat810
6
6
  Author-email: 67593116+humblemat810@users.noreply.github.com
@@ -65,6 +65,19 @@ The reusable helpers live under `src/workflow_ingest/` and are designed so the
65
65
  same core logic can be called from tests, scripts, and higher-level workflow
66
66
  code without duplicating orchestration.
67
67
 
68
+ Layerwise parsing selects a strategy independently for each frontier layer.
69
+ The default deterministic order is `layer_excerpt`, `layer_boundary`, then
70
+ `page_index`; callers may provide another complete order, and optional triage
71
+ may choose among the currently enabled strategies. Failed strategies are
72
+ disabled for that layer and the workflow routes through the remaining methods
73
+ before reaching explicit parse failure. PageIndex is a one-layer structural
74
+ fallback that preserves exact source pointers and returns expandable children
75
+ to normal strategy selection. See the
76
+ [`0.2.4` release note](doc/release_0.2.4.md) and the
77
+ [progressive refinement ADR](doc/adr_progressive_refinement_strategy_arbitration.md).
78
+ The deterministic and text-only Bonsai fixture evaluation is recorded in the
79
+ [`PageIndex adversarial fixture report`](doc/page_index_adversarial_fixture_report.md).
80
+
68
81
  For an adoption path that starts with parser-grounded source units and later
69
82
  adds LLM-Wiki cross-document maintenance, see
70
83
  [`doc/progressive_adoption_guide.md`](doc/progressive_adoption_guide.md). The
@@ -14,29 +14,30 @@ kg_doc_parser/utils/file_loaders.py,sha256=Ksj9wh5Vry4FuWZbDxEeAR3WBCZJQXpn0Rs7X
14
14
  kg_doc_parser/utils/langchain.py,sha256=n_51Q9mUeodkGvBl8JwAaBFD2YKxDXmdvgBQhznhBA0,11559
15
15
  kg_doc_parser/utils/log.py,sha256=3OwyrioXK3JRnP27VhLT41a47_YzMif-S9SupsE4avI,4711
16
16
  kg_doc_parser/utils/version_chaining.py,sha256=5B3p-M-UgzOEvjxeEuUFIV7Gk0lvJvkbUTTK8MGkyTc,55321
17
- kg_doc_parser/workflow_ingest/__init__.py,sha256=HcRkDwFi2KDVviIsYbN-XtFMsuR27yvd6G_azddIkhQ,5516
17
+ kg_doc_parser/workflow_ingest/__init__.py,sha256=7yWFNL3fpIsWjU0booCix7OfQwiHhgSsynKy2b_lpCc,6119
18
18
  kg_doc_parser/workflow_ingest/_kogwistar.py,sha256=YI4aWToJt2VELxrp5NWUyjp9_n1_COuP4QulSP5FMk4,454
19
19
  kg_doc_parser/workflow_ingest/adapters.py,sha256=VvLiYXnXAGvVsk1uh_IlRf_sPJ37ZuI-GGYmUiFM5C8,8538
20
20
  kg_doc_parser/workflow_ingest/cache.py,sha256=a4XbjT9LJfb7ksy9DtM9MLNsLlf3e5oEl--SAT0dZzo,2125
21
- kg_doc_parser/workflow_ingest/cli.py,sha256=ugvXRqDvktUcUUgNt6MVsa0tczkmAF5cLi-LxeQ7M_8,12858
21
+ kg_doc_parser/workflow_ingest/cli.py,sha256=yqQ93y7tFovH78qeMSXk2xYSFdYLxsNp_s_MXt_JkbM,14881
22
22
  kg_doc_parser/workflow_ingest/clients.py,sha256=z5yrzS7St940GNZCo1Knnx81uoIEi5bUhrmc1AFESe0,19146
23
23
  kg_doc_parser/workflow_ingest/demo_harness.py,sha256=e38a1xUAORy2ruv-XVoVvovlFSSyvALq4xRkV1IIozc,17164
24
- kg_doc_parser/workflow_ingest/design.py,sha256=4QJ7DSMD_4BhKRK090nnG4rMo5KAiiZY1iqD9ue42jM,7525
25
- kg_doc_parser/workflow_ingest/handlers.py,sha256=0CZCBve7vfRsJfUaPh3FeLrHWTWBUcQHG8fD3U6Ja-M,32695
24
+ kg_doc_parser/workflow_ingest/design.py,sha256=IY8cPb-bQuoMD49a7HtSzzG_6VZN9a32URtgJ-nmedc,8649
25
+ kg_doc_parser/workflow_ingest/handlers.py,sha256=FS0oH5pTP7Uj142NHNCF9uAmX1crpbw7e7N7we8pt98,47403
26
26
  kg_doc_parser/workflow_ingest/layered_contracts.py,sha256=CBeSMkPwgLUrx77w9yEg8oxXMNVvbpP_TAicmliiuB0,10672
27
- kg_doc_parser/workflow_ingest/layerwise_llm.py,sha256=75bw6aWfq1pJ1q6xeKL2sR6o6MfI358yEHLtJj0fVbc,118765
28
- kg_doc_parser/workflow_ingest/models.py,sha256=oW6CAP9Vt4d5ElGOO6QesMoTd8EovSOKSdnpdMksjPY,29481
29
- kg_doc_parser/workflow_ingest/ocr_pipeline.py,sha256=4CzXKad9C52Zzzt4zu_x5U6UrrcZi52Yr54ISJlRIok,57198
30
- kg_doc_parser/workflow_ingest/page_index.py,sha256=Im9L_KW1tjiKhUb6w46XinkcwlEF2iTmpaKM7VYJMWg,83988
31
- kg_doc_parser/workflow_ingest/parser_core.py,sha256=ooMDFvDVA0EVYE2eFSG5fjiWLC5nKrM-gVlZ8wPGdUQ,39910
32
- kg_doc_parser/workflow_ingest/parsing.py,sha256=HENVUdIyM0EcMA3jTpsWKTvkB3E5lL0Ox0xU9geAETA,8459
27
+ kg_doc_parser/workflow_ingest/layerwise_llm.py,sha256=5M-cGsqERi7bGn3_F-my9CgT9K2ifjZmWq4G5Ei6VBI,119376
28
+ kg_doc_parser/workflow_ingest/models.py,sha256=EchHIz-lAXxGNjnZyldwoq93jRInnGdS0Ls_Owgjnn8,31754
29
+ kg_doc_parser/workflow_ingest/ocr_pipeline.py,sha256=4B9pFfnA_CjBGNbj8mMjmg14ysgfdN5NiYZyhe0dhfM,57411
30
+ kg_doc_parser/workflow_ingest/page_index.py,sha256=llwsiD_rwXogsQ3ha1aFaTJM-hu9vt9BePgdToQo6lE,93123
31
+ kg_doc_parser/workflow_ingest/parser_core.py,sha256=plbqFtm_Lwlg4C5hWWE3VYczmtmH0FHbQh4bGh5loAc,41317
32
+ kg_doc_parser/workflow_ingest/parsing.py,sha256=A-ocoWIIyoar5Rk-mCiWbAYuT0-HQjZQPiB6UK5RelM,8567
33
33
  kg_doc_parser/workflow_ingest/probe.py,sha256=6bQo6ajJFAFIczV26H70FNZL-Aj4xistm6HAzsK7sus,6240
34
- kg_doc_parser/workflow_ingest/providers.py,sha256=k0GiRM3WfjCjU1F9Of_4SxPZqv0uHHuWU6YuOXD0Ft4,26789
34
+ kg_doc_parser/workflow_ingest/providers.py,sha256=t6fhfN6T0UjknRZMJoquSjz-kPuSPdhpuXoH8JdXdMI,30550
35
35
  kg_doc_parser/workflow_ingest/runners.py,sha256=LXdb_lztn3__xwGj3m4I_1zDhyNCngmrt12oN50qMJE,19636
36
- kg_doc_parser/workflow_ingest/semantics.py,sha256=3s_F10_aP2gvtooCojuHZtZ1NmNxBmET68WOAF_s3BM,8168
37
- kg_doc_parser/workflow_ingest/service.py,sha256=uL3XVrtF6roAjpY3Q6Fho2Q7VxNMNMYl17_vQf-vJKc,4593
36
+ kg_doc_parser/workflow_ingest/semantics.py,sha256=SbqZ4VA2592lmSnTtSHewOP7cMyz6GM97PXnWh_5yXM,9057
37
+ kg_doc_parser/workflow_ingest/service.py,sha256=4V1abu9vKsxRt_F06ecprd74wXpQJT0XFrd-i1tfejc,7746
38
38
  kg_doc_parser/workflow_ingest/smoke_assets.py,sha256=FA6JSUBAIZFPUz52G3UrQByMLrQhC8N9NinWXYDmXYk,1978
39
- graph_knowledge_doc_parser-0.2.2.dist-info/METADATA,sha256=mwbydxTV9jbGTLonLuJe8QXP2ArJ6-jNEbK9YJP44Uk,15554
40
- graph_knowledge_doc_parser-0.2.2.dist-info/WHEEL,sha256=L-WvLdSBvJWHXv5zrQxDvNRnIgoBzLUpcbuiw19KCuQ,88
41
- graph_knowledge_doc_parser-0.2.2.dist-info/entry_points.txt,sha256=AXIuydpwsOveH3S3HRlyJ8gphiKOznJc1c_f2rNZNV0,74
42
- graph_knowledge_doc_parser-0.2.2.dist-info/RECORD,,
39
+ kg_doc_parser/workflow_ingest/strategy.py,sha256=bInKf2dUojdE4Ydw0GxhqtN4SG5sDHcEJDJLqisTg_I,8419
40
+ graph_knowledge_doc_parser-0.2.4.dist-info/METADATA,sha256=9RjcyOw4rzlPYj2AFKVhd_9DhKbwkNHsZ4jMyjr6tvg,16431
41
+ graph_knowledge_doc_parser-0.2.4.dist-info/WHEEL,sha256=L-WvLdSBvJWHXv5zrQxDvNRnIgoBzLUpcbuiw19KCuQ,88
42
+ graph_knowledge_doc_parser-0.2.4.dist-info/entry_points.txt,sha256=AXIuydpwsOveH3S3HRlyJ8gphiKOznJc1c_f2rNZNV0,74
43
+ graph_knowledge_doc_parser-0.2.4.dist-info/RECORD,,
@@ -4,71 +4,34 @@ from .adapters import (
4
4
  build_parser_source_map,
5
5
  normalize_ocr_pages,
6
6
  )
7
+ from .cache import WorkflowLLMCallCache
7
8
  from .clients import (
8
9
  CanonicalGraphPersistenceClient,
9
- DocumentTreeApiPersistenceClient,
10
10
  DirectRuntimeIngestClient,
11
+ DocumentTreeApiPersistenceClient,
11
12
  IngestExecutionClient,
12
13
  ServerCanonicalKgClient,
13
14
  UnsupportedClientOperation,
14
15
  )
15
- from .cache import WorkflowLLMCallCache
16
- from .design import DEFAULT_WORKFLOW_ID, build_ingest_workflow_design, ensure_ingest_workflow_design
17
16
  from .demo_harness import DemoHarnessArtifacts, DemoHarnessConfig, run_demo_harness
18
- from .handlers import build_ingest_step_resolver
19
- from .parsing import (
20
- OCRParseRequest,
21
- PageIndexParseRequest,
22
- ParseMode,
23
- TreeParseRequest,
24
- parse_document,
25
- parse_ocr_document,
26
- parse_page_index_document,
27
- parse_tree_document,
28
- )
29
- from .ocr_pipeline import (
30
- OCRImagePayload,
31
- OCRWorkflowArtifacts,
32
- OCRWorkflowStateStore,
33
- prepare_ocr_workflow_input,
34
- run_ocr_ingest_workflow,
35
- )
36
- from .runners import (
37
- LayerwiseWorkflowCommandResult,
38
- OcrWorkflowCommandResult,
39
- PageIndexWorkflowCommandResult,
40
- WorkflowCommandResult,
41
- discover_input_files,
42
- run_demo_harness_workflow,
43
- run_layerwise_batch_workflow,
44
- run_layerwise_source_workflow,
45
- run_ocr_batch_workflow,
46
- run_ocr_source_workflow,
47
- run_page_index_batch_workflow,
48
- run_page_index_source_workflow,
49
- )
50
- from .smoke_assets import generate_ocr_smoke_assets
51
- from .page_index import (
52
- BlockAssignment,
53
- BlockAssignmentBatch,
54
- CandidateBlock,
55
- PageIndexBlockSpec,
56
- PageIndexParseResult,
57
- PageIndexValidationResult,
58
- build_page_index_workflow_input,
17
+ from .design import (
18
+ DEFAULT_WORKFLOW_ID,
19
+ build_ingest_workflow_design,
20
+ ensure_ingest_workflow_design,
59
21
  )
22
+ from .handlers import build_ingest_step_resolver
60
23
  from .models import (
61
24
  BoundingBox,
62
25
  CanonicalGraphWriteResult,
63
26
  CurrentLayerContext,
64
- CurrentLayerReview,
65
27
  CurrentLayerResult,
66
- LayerCoverageGap,
67
- LayerDuplicateChildNote,
28
+ CurrentLayerReview,
68
29
  GroundedSourceRecord,
69
30
  IngestRunHandle,
70
31
  IngestRunResult,
71
32
  LayerChildCandidate,
33
+ LayerCoverageGap,
34
+ LayerDuplicateChildNote,
72
35
  LayerFrontierItem,
73
36
  LayerSpanConflict,
74
37
  NormalizedPage,
@@ -79,16 +42,23 @@ from .models import (
79
42
  WorkflowExportBundle,
80
43
  WorkflowIngestInput,
81
44
  )
82
- from .providers import (
83
- EmbeddingProviderConfig,
84
- FakeChatModel,
85
- ProviderEndpointConfig,
86
- WorkflowProviderSettings,
87
- build_chat_model,
88
- build_chat_model_for_role,
89
- build_embedding_function,
45
+ from .ocr_pipeline import (
46
+ OCRImagePayload,
47
+ OCRWorkflowArtifacts,
48
+ OCRWorkflowStateStore,
49
+ prepare_ocr_workflow_input,
50
+ run_ocr_ingest_workflow,
51
+ )
52
+ from .page_index import (
53
+ BlockAssignment,
54
+ BlockAssignmentBatch,
55
+ CandidateBlock,
56
+ PageIndexBlockSpec,
57
+ PageIndexParseResult,
58
+ PageIndexValidationResult,
59
+ build_page_index_workflow_input,
60
+ parse_page_index_layer,
90
61
  )
91
- from .probe import WorkflowProbe, emit_probe_event
92
62
  from .parser_core import (
93
63
  apply_cud_update,
94
64
  check_layer_coverage,
@@ -99,101 +69,157 @@ from .parser_core import (
99
69
  propose_layer_breakdown,
100
70
  review_layer,
101
71
  )
102
- from .service import build_default_engines, build_runtime, run_ingest_workflow
72
+ from .parsing import (
73
+ OCRParseRequest,
74
+ PageIndexParseRequest,
75
+ ParseMode,
76
+ TreeParseRequest,
77
+ parse_document,
78
+ parse_ocr_document,
79
+ parse_page_index_document,
80
+ parse_tree_document,
81
+ )
82
+ from .probe import WorkflowProbe, emit_probe_event
83
+ from .providers import (
84
+ EmbeddingProviderConfig,
85
+ FakeChatModel,
86
+ ProviderEndpointConfig,
87
+ WorkflowProviderSettings,
88
+ build_chat_model,
89
+ build_chat_model_for_role,
90
+ build_embedding_function,
91
+ )
92
+ from .runners import (
93
+ LayerwiseWorkflowCommandResult,
94
+ OcrWorkflowCommandResult,
95
+ PageIndexWorkflowCommandResult,
96
+ WorkflowCommandResult,
97
+ discover_input_files,
98
+ run_demo_harness_workflow,
99
+ run_layerwise_batch_workflow,
100
+ run_layerwise_source_workflow,
101
+ run_ocr_batch_workflow,
102
+ run_ocr_source_workflow,
103
+ run_page_index_batch_workflow,
104
+ run_page_index_source_workflow,
105
+ )
103
106
  from .semantics import HydratedTextPointer, SemanticNode
107
+ from .service import build_default_engines, build_runtime, run_ingest_workflow
108
+ from .smoke_assets import generate_ocr_smoke_assets
109
+ from .strategy import (
110
+ HARD_CODED_STRATEGY_PRIORITY,
111
+ ParseStrategy,
112
+ ParseStrategyAssessment,
113
+ ParseStrategyDecision,
114
+ ParseStrategyRequest,
115
+ ParseStrategyTriage,
116
+ build_llm_strategy_triage,
117
+ hardcoded_strategy,
118
+ select_parse_strategy,
119
+ )
104
120
 
105
121
  __all__ = [
122
+ "DEFAULT_WORKFLOW_ID",
123
+ "HARD_CODED_STRATEGY_PRIORITY",
124
+ "BlockAssignment",
125
+ "BlockAssignmentBatch",
106
126
  "BoundingBox",
127
+ "CandidateBlock",
107
128
  "CanonicalGraphPersistenceClient",
108
129
  "CanonicalGraphWriteResult",
109
130
  "CurrentLayerContext",
110
- "CurrentLayerReview",
111
131
  "CurrentLayerResult",
112
- "LayerCoverageGap",
113
- "LayerDuplicateChildNote",
132
+ "CurrentLayerReview",
114
133
  "DemoHarnessArtifacts",
115
134
  "DemoHarnessConfig",
116
- "DocumentTreeApiPersistenceClient",
117
135
  "DirectRuntimeIngestClient",
136
+ "DocumentTreeApiPersistenceClient",
118
137
  "EmbeddingProviderConfig",
138
+ "FakeChatModel",
119
139
  "GroundedSourceRecord",
120
140
  "HydratedTextPointer",
121
- "FakeChatModel",
122
141
  "IngestExecutionClient",
123
142
  "IngestRunHandle",
124
143
  "IngestRunResult",
125
144
  "LayerChildCandidate",
145
+ "LayerCoverageGap",
146
+ "LayerDuplicateChildNote",
126
147
  "LayerFrontierItem",
127
148
  "LayerSpanConflict",
149
+ "LayerwiseWorkflowCommandResult",
128
150
  "NormalizedPage",
129
151
  "NormalizedSourceCollection",
130
152
  "OCRImagePayload",
153
+ "OCRParseRequest",
131
154
  "OCRWorkflowArtifacts",
132
155
  "OCRWorkflowStateStore",
133
- "OCRParseRequest",
134
- "ParseSessionState",
135
- "ProviderEndpointConfig",
136
- "PageIndexParseRequest",
156
+ "OcrWorkflowCommandResult",
137
157
  "PageIndexBlockSpec",
158
+ "PageIndexParseRequest",
138
159
  "PageIndexParseResult",
139
160
  "PageIndexValidationResult",
161
+ "PageIndexWorkflowCommandResult",
140
162
  "ParseMode",
141
- "TreeParseRequest",
163
+ "ParseSessionState",
164
+ "ParseStrategy",
165
+ "ParseStrategyAssessment",
166
+ "ParseStrategyDecision",
167
+ "ParseStrategyRequest",
168
+ "ParseStrategyTriage",
169
+ "ProviderEndpointConfig",
142
170
  "SemanticNode",
143
171
  "ServerCanonicalKgClient",
144
172
  "SourceUnit",
173
+ "TreeParseRequest",
145
174
  "UnsupportedClientOperation",
146
175
  "ValidationReport",
176
+ "WorkflowCommandResult",
177
+ "WorkflowExportBundle",
178
+ "WorkflowIngestInput",
147
179
  "WorkflowLLMCallCache",
148
180
  "WorkflowProbe",
149
181
  "WorkflowProviderSettings",
150
- "WorkflowExportBundle",
151
- "WorkflowIngestInput",
152
- "WorkflowCommandResult",
153
- "OcrWorkflowCommandResult",
154
- "PageIndexWorkflowCommandResult",
155
- "LayerwiseWorkflowCommandResult",
156
- "CandidateBlock",
157
- "BlockAssignment",
158
- "BlockAssignmentBatch",
159
- "DEFAULT_WORKFLOW_ID",
160
- "build_chat_model",
161
- "build_chat_model_for_role",
162
- "build_embedding_function",
163
182
  "apply_cud_update",
164
- "check_layer_coverage",
165
- "default_parse_semantic_fn",
166
- "finalize_semantic_tree",
167
- "initialize_parse_session",
168
- "prepare_layer_frontier",
169
- "propose_layer_breakdown",
170
- "review_layer",
171
183
  "build_authoritative_source_map",
184
+ "build_chat_model",
185
+ "build_chat_model_for_role",
172
186
  "build_default_engines",
173
- "build_ingest_workflow_design",
187
+ "build_embedding_function",
174
188
  "build_ingest_step_resolver",
175
- "build_parser_input_dict",
189
+ "build_ingest_workflow_design",
190
+ "build_llm_strategy_triage",
176
191
  "build_page_index_workflow_input",
192
+ "build_parser_input_dict",
177
193
  "build_parser_source_map",
178
194
  "build_runtime",
179
- "ensure_ingest_workflow_design",
195
+ "check_layer_coverage",
196
+ "default_parse_semantic_fn",
197
+ "discover_input_files",
180
198
  "emit_probe_event",
199
+ "ensure_ingest_workflow_design",
200
+ "finalize_semantic_tree",
201
+ "generate_ocr_smoke_assets",
202
+ "hardcoded_strategy",
203
+ "initialize_parse_session",
181
204
  "normalize_ocr_pages",
182
205
  "parse_document",
183
206
  "parse_ocr_document",
184
207
  "parse_page_index_document",
208
+ "parse_page_index_layer",
185
209
  "parse_tree_document",
210
+ "prepare_layer_frontier",
186
211
  "prepare_ocr_workflow_input",
212
+ "propose_layer_breakdown",
213
+ "review_layer",
187
214
  "run_demo_harness",
188
215
  "run_demo_harness_workflow",
189
216
  "run_ingest_workflow",
190
- "discover_input_files",
191
217
  "run_layerwise_batch_workflow",
192
218
  "run_layerwise_source_workflow",
193
219
  "run_ocr_batch_workflow",
220
+ "run_ocr_ingest_workflow",
194
221
  "run_ocr_source_workflow",
195
222
  "run_page_index_batch_workflow",
196
223
  "run_page_index_source_workflow",
197
- "generate_ocr_smoke_assets",
198
- "run_ocr_ingest_workflow",
224
+ "select_parse_strategy",
199
225
  ]
@@ -46,14 +46,40 @@ def _provider_settings_from_args(args: argparse.Namespace) -> WorkflowProviderSe
46
46
  "api_key_env": args.parser_api_key_env,
47
47
  }
48
48
  proposal_mode = getattr(args, "proposal_mode", None)
49
+ parse_strategy = getattr(args, "parse_strategy", None)
50
+ parse_strategy_order = getattr(args, "parse_strategy_order", None)
51
+ triage_enabled = getattr(args, "triage_enabled", None)
52
+ page_index_summary_enabled = getattr(args, "page_index_summary_enabled", None)
49
53
  ocr_override = any(value is not None for value in ocr_values.values())
50
54
  parser_override = any(value is not None for value in parser_values.values())
51
55
  proposal_override = proposal_mode is not None
52
- if not ocr_override and not parser_override and not proposal_override:
56
+ strategy_override = parse_strategy is not None
57
+ strategy_order_override = parse_strategy_order is not None
58
+ triage_override = triage_enabled is not None
59
+ summary_override = page_index_summary_enabled is not None
60
+ if (
61
+ not ocr_override
62
+ and not parser_override
63
+ and not proposal_override
64
+ and not strategy_override
65
+ and not strategy_order_override
66
+ and not triage_override
67
+ and not summary_override
68
+ ):
53
69
  return None
54
70
  settings = WorkflowProviderSettings.from_env()
55
71
  if proposal_override:
56
72
  settings = settings.model_copy(update={"proposal_mode": proposal_mode})
73
+ if strategy_override:
74
+ settings = settings.model_copy(update={"parse_strategy": parse_strategy})
75
+ if strategy_order_override:
76
+ settings = settings.model_copy(
77
+ update={"parse_strategy_order": tuple(parse_strategy_order.split(","))}
78
+ )
79
+ if triage_override:
80
+ settings = settings.model_copy(update={"triage_enabled": triage_enabled})
81
+ if summary_override:
82
+ settings = settings.model_copy(update={"page_index_summary_enabled": page_index_summary_enabled})
57
83
  if ocr_override:
58
84
  settings = settings.model_copy(
59
85
  update={
@@ -76,6 +102,29 @@ def _provider_settings_from_args(args: argparse.Namespace) -> WorkflowProviderSe
76
102
  def _add_provider_args(parser: argparse.ArgumentParser) -> None:
77
103
  group = parser.add_argument_group("provider overrides")
78
104
  group.add_argument("--proposal-mode", choices=["children", "boundaries"], default=None)
105
+ group.add_argument(
106
+ "--parse-strategy",
107
+ choices=["auto", "layer_excerpt", "layer_boundary", "page_index"],
108
+ default=None,
109
+ help="Select the parser strategy for every frontier layer in this parse call",
110
+ )
111
+ group.add_argument(
112
+ "--parse-strategy-order",
113
+ default=None,
114
+ help="comma-separated parser order, e.g. layer_boundary,layer_excerpt,page_index",
115
+ )
116
+ group.add_argument(
117
+ "--triage-enabled",
118
+ action=argparse.BooleanOptionalAction,
119
+ default=None,
120
+ help="Enable or disable provider-backed per-layer strategy triage",
121
+ )
122
+ group.add_argument(
123
+ "--page-index-summary-enabled",
124
+ action=argparse.BooleanOptionalAction,
125
+ default=None,
126
+ help="Enable or disable summaries on PageIndex nodes for this parse call",
127
+ )
79
128
  group.add_argument("--ocr-provider", default=None)
80
129
  group.add_argument("--ocr-model", default=None)
81
130
  group.add_argument("--ocr-base-url", default=None)
@@ -58,6 +58,8 @@ def _workflow_edge(
58
58
  edge_id: str,
59
59
  src: str,
60
60
  dst: str,
61
+ predicate: str | None = None,
62
+ is_default: bool = True,
61
63
  ) -> WorkflowEdge:
62
64
  dst_name = dst.split("|")[-1]
63
65
  return WorkflowEdge(
@@ -76,7 +78,8 @@ def _workflow_edge(
76
78
  "entity_type": "workflow_edge",
77
79
  "workflow_id": workflow_id,
78
80
  "wf_priority": 100,
79
- "wf_is_default": True,
81
+ "wf_predicate": predicate,
82
+ "wf_is_default": is_default,
80
83
  "wf_multiplicity": "one",
81
84
  "wf_version": "v1",
82
85
  },
@@ -97,12 +100,14 @@ def build_ingest_workflow_design(
97
100
  ("init_parse_session", "init_parse_session", False, False),
98
101
  ("check_frontier_remaining", "check_frontier_remaining", False, False),
99
102
  ("prepare_layer_frontier", "prepare_layer_frontier", False, False),
100
- ("propose_layer_breakdown", "propose_layer_breakdown", False, False),
103
+ ("triage_parse_strategy", "triage_parse_strategy", False, False),
104
+ ("layer_excerpt_method", "propose_layer_breakdown", False, False),
105
+ ("layer_boundary_method", "propose_layer_breakdown", False, False),
106
+ ("page_index_layer", "page_index_layer", False, False),
101
107
  ("review_cud_proposal", "review_cud_proposal", False, False),
102
108
  ("apply_cud_update", "apply_cud_update", False, False),
103
109
  ("check_layer_coverage", "check_layer_coverage", False, False),
104
110
  ("check_layer_satisfaction", "check_layer_satisfaction", False, False),
105
- ("switch_split_strategy", "switch_split_strategy", False, False),
106
111
  ("repair_layer_pointers", "repair_layer_pointers", False, False),
107
112
  ("dedupe_and_filter_layer", "dedupe_and_filter_layer", False, False),
108
113
  ("commit_layer_children", "commit_layer_children", False, False),
@@ -112,6 +117,7 @@ def build_ingest_workflow_design(
112
117
  ("validate_tree", "validate_tree", False, False),
113
118
  ("export_graph", "export_graph", False, False),
114
119
  ("persist_canonical_graph", "persist_canonical_graph", False, False),
120
+ ("parse_failure", "parse_failure", False, True),
115
121
  ("end", "end", False, True),
116
122
  ]
117
123
  nodes = [
@@ -125,22 +131,26 @@ def build_ingest_workflow_design(
125
131
  for suffix, op, start, terminal in node_specs
126
132
  ]
127
133
  node_by_suffix = {node.safe_get_id().split("|")[-1]: node for node in nodes}
128
- edge_pairs: Iterable[tuple[str, str]] = [
134
+ edge_pairs: Iterable[tuple[str, str] | tuple[str, str, str]] = [
129
135
  ("start", "normalize_input"),
130
136
  ("normalize_input", "build_source_map"),
131
137
  ("build_source_map", "init_parse_session"),
132
138
  ("init_parse_session", "check_frontier_remaining"),
133
139
  ("check_frontier_remaining", "prepare_layer_frontier"),
134
140
  ("check_frontier_remaining", "finalize_semantic_tree"),
135
- ("prepare_layer_frontier", "propose_layer_breakdown"),
136
- ("propose_layer_breakdown", "review_cud_proposal"),
141
+ ("prepare_layer_frontier", "triage_parse_strategy"),
142
+ ("triage_parse_strategy", "layer_excerpt_method", "parse_strategy_layer_excerpt"),
143
+ ("triage_parse_strategy", "layer_boundary_method", "parse_strategy_layer_boundary"),
144
+ ("triage_parse_strategy", "page_index_layer", "parse_strategy_page_index"),
145
+ ("triage_parse_strategy", "parse_failure", "strategy_selection_failed"),
146
+ ("layer_excerpt_method", "review_cud_proposal", "strategy_attempted"),
147
+ ("layer_boundary_method", "review_cud_proposal", "strategy_attempted"),
137
148
  ("review_cud_proposal", "apply_cud_update"),
138
149
  ("apply_cud_update", "check_layer_coverage"),
139
150
  ("check_layer_coverage", "check_layer_satisfaction"),
140
- ("check_layer_satisfaction", "propose_layer_breakdown"),
141
- ("check_layer_satisfaction", "switch_split_strategy"),
142
- ("switch_split_strategy", "propose_layer_breakdown"),
143
- ("check_layer_satisfaction", "repair_layer_pointers"),
151
+ ("check_layer_satisfaction", "repair_layer_pointers", "layer_satisfied"),
152
+ ("check_layer_satisfaction", "triage_parse_strategy", "strategy_failed_with_remaining"),
153
+ ("check_layer_satisfaction", "parse_failure", "all_strategies_exhausted"),
144
154
  ("repair_layer_pointers", "dedupe_and_filter_layer"),
145
155
  ("dedupe_and_filter_layer", "commit_layer_children"),
146
156
  ("commit_layer_children", "check_children_expandable"),
@@ -148,6 +158,7 @@ def build_ingest_workflow_design(
148
158
  ("check_children_expandable", "check_frontier_remaining"),
149
159
  ("enqueue_next_layer_frontier", "check_frontier_remaining"),
150
160
  ("finalize_semantic_tree", "validate_tree"),
161
+ ("page_index_layer", "review_cud_proposal", "strategy_attempted"),
151
162
  ("validate_tree", "export_graph"),
152
163
  ("export_graph", "persist_canonical_graph"),
153
164
  ("persist_canonical_graph", "end"),
@@ -155,11 +166,15 @@ def build_ingest_workflow_design(
155
166
  edges = [
156
167
  _workflow_edge(
157
168
  workflow_id=workflow_id,
158
- edge_id=f"wf|{workflow_id}|e|{src}->{dst}",
169
+ edge_id=f"wf|{workflow_id}|e|{index}|{src}->{dst}",
159
170
  src=node_by_suffix[src].safe_get_id(),
160
171
  dst=node_by_suffix[dst].safe_get_id(),
172
+ predicate=predicate if len(edge_spec) == 3 else None,
173
+ is_default=len(edge_spec) == 2,
161
174
  )
162
- for src, dst in edge_pairs
175
+ for index, edge_spec in enumerate(edge_pairs)
176
+ for src, dst, *predicate_values in [edge_spec]
177
+ for predicate in [predicate_values[0] if predicate_values else None]
163
178
  ]
164
179
  return nodes, edges
165
180