graph-knowledge-doc-parser 0.2.2__tar.gz → 0.2.4__tar.gz

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 (42) hide show
  1. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/PKG-INFO +14 -1
  2. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/README.md +13 -0
  3. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/__init__.py +119 -93
  4. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/cli.py +50 -1
  5. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/design.py +27 -12
  6. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/handlers.py +341 -32
  7. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/layerwise_llm.py +21 -3
  8. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/models.py +61 -1
  9. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/ocr_pipeline.py +15 -11
  10. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/page_index.py +236 -24
  11. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/parser_core.py +33 -3
  12. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/parsing.py +3 -0
  13. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/providers.py +94 -0
  14. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/semantics.py +16 -3
  15. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/service.py +74 -2
  16. graph_knowledge_doc_parser-0.2.4/kg_doc_parser/workflow_ingest/strategy.py +209 -0
  17. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/pyproject.toml +1 -1
  18. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/__init__.py +0 -0
  19. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/cast_hinting.py +0 -0
  20. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/document_ingest_log_config.py +0 -0
  21. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/document_ingester_logger.py +0 -0
  22. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/llm_structured_output.py +0 -0
  23. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/models.py +0 -0
  24. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/ocr.py +0 -0
  25. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/pdf2png.py +0 -0
  26. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/semantic_document_splitting_layerwise_edits.py +0 -0
  27. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/text_processing_utils.py +0 -0
  28. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/utils/__init__.py +0 -0
  29. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/utils/bounded_threadpool_executor.py +0 -0
  30. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/utils/file_loaders.py +0 -0
  31. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/utils/langchain.py +0 -0
  32. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/utils/log.py +0 -0
  33. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/utils/version_chaining.py +0 -0
  34. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/_kogwistar.py +0 -0
  35. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/adapters.py +0 -0
  36. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/cache.py +0 -0
  37. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/clients.py +0 -0
  38. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/demo_harness.py +0 -0
  39. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/layered_contracts.py +0 -0
  40. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/probe.py +0 -0
  41. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/runners.py +0 -0
  42. {graph_knowledge_doc_parser-0.2.2 → graph_knowledge_doc_parser-0.2.4}/kg_doc_parser/workflow_ingest/smoke_assets.py +0 -0
@@ -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
@@ -32,6 +32,19 @@ The reusable helpers live under `src/workflow_ingest/` and are designed so the
32
32
  same core logic can be called from tests, scripts, and higher-level workflow
33
33
  code without duplicating orchestration.
34
34
 
35
+ Layerwise parsing selects a strategy independently for each frontier layer.
36
+ The default deterministic order is `layer_excerpt`, `layer_boundary`, then
37
+ `page_index`; callers may provide another complete order, and optional triage
38
+ may choose among the currently enabled strategies. Failed strategies are
39
+ disabled for that layer and the workflow routes through the remaining methods
40
+ before reaching explicit parse failure. PageIndex is a one-layer structural
41
+ fallback that preserves exact source pointers and returns expandable children
42
+ to normal strategy selection. See the
43
+ [`0.2.4` release note](doc/release_0.2.4.md) and the
44
+ [progressive refinement ADR](doc/adr_progressive_refinement_strategy_arbitration.md).
45
+ The deterministic and text-only Bonsai fixture evaluation is recorded in the
46
+ [`PageIndex adversarial fixture report`](doc/page_index_adversarial_fixture_report.md).
47
+
35
48
  For an adoption path that starts with parser-grounded source units and later
36
49
  adds LLM-Wiki cross-document maintenance, see
37
50
  [`doc/progressive_adoption_guide.md`](doc/progressive_adoption_guide.md). The
@@ -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