Commit 33cd343
fix(enrich): propagate task to enricher and enforce label whitelist (#31)
* fix(enrich): propagate task to enricher and enforce label whitelist
Two related bugs prevented YAML-declared entity-type constraints
from reaching NuExtract:
1. `orchestrator._ensure_enriched` hard-coded `task=""` when the
YAML used the `operations:` shortcut, dropping `task.query` on
the floor. With no task, `_infer_entity_targets` returned None,
so the prompt sent to the model never included the allowed
labels. Now `_ensure_enriched` accepts `task` and both
`_run_enrich` and `_run_rag` pass `task.query` through.
2. `enricher._generate_entities` only applied `entity_targets`
as a soft hint, and had no post-filter on returned labels.
When the model invented types (Project, Software, Algorithm,
IP-Address, …) they flowed through into the rendered output.
Now the prompt carries a HARD CONSTRAINT block listing the
allowed labels, and the parser drops any mention whose
case-folded label is not in the allowed set (with an INFO
log line summarising drops per call).
Verified on df3 with the `2026-05-22a_enrich_nuextract3_postpatch`
run that the enricher-only patch was a no-op without (1); end-to-end
re-run with both fixes is queued.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Ana Daniele <ana.daniele@ibm.com>
* fix(enrich): accept raw JSON from rewrite LLM in _infer_entity_targets
`_validate_entity_target_spec` (and the post-validation parse on
line 688) used `find_json_dicts`, which only matches JSON wrapped
in a ```json ...``` markdown block. NuExtract3 ignores that part
of the prompt and returns a bare JSON object instead — well-formed,
just unfenced. Validation then failed and `_infer_entity_targets`
returned None, so `entity_targets` reached `_generate_entities`
as None and the HARD CONSTRAINT clause from the previous commit
was silently skipped.
Introduce `_parse_spec_dict` inside `_infer_entity_targets`: try
`find_json_dicts` first (preserves existing behaviour for models
that do use the fence), and fall back to `json.loads(content.strip())`
when no fence is found. Both the validation hook and the final
parse use the same helper.
Confirmed end-to-end on df3 (`2026-06-01b_..._postpatch_v3`): the
brief now parses, `entity_targets["labels"] = ["MODEL", "DATASET",
"KPI"]`, and every per-chunk LLM REQUEST carries the HARD CONSTRAINT
block.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Ana Daniele <ana.daniele@ibm.com>
* fix(enrich): isolate sessions, tolerate raw entity responses, use docling-core fields
Three issues surfaced once the prompt actually carried the
HARD CONSTRAINT block and NuExtract started responding with
the expected schema.
1. Session bleed. `_detect_key_entities` reused one LM Studio
session for both `_infer_entity_targets` (which primes the
model with the spec schema `{"generic":..., "labels":...}`)
and the per-chunk extraction calls. NuExtract carried the
prior turn's schema forward and answered every chunk with
the spec dict instead of an entity array. Now the leaf
stage opens its own session via `_create_extraction_session`.
2. Entity parser too strict. The response parser required a
```json ...``` fenced block and assumed the top-level payload
was a list. NuExtract returns bare JSON, usually as
`{"entities": [...]}`. Fall back to `result.strip()` when no
fence is present and unwrap a single `entities` key into the
list before iterating; coerce other shapes to an empty list
so downstream code keeps its invariants.
3. EntityMention field names. The mention constructor used the
old docling-core argument names (`original=`, `span=`). The
current docling-core EntityMention expects `orig=` and
`charspan=`; the mismatch raised a pydantic validation error
on every successful response, which the surrounding `try`
swallowed as "Failed to parse entities JSON".
End-to-end on df3: `2026-06-01e_..._postpatch_v6` is the first
run where parsed entities are non-empty, e.g. `[EntityMention(
text='Nougat', label='MODEL', charspan=(0, 6)), ...]`, with
zero pydantic warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Ana Daniele <ana.daniele@ibm.com>
* style: fix formatting
Signed-off-by: Cesar Berrospi Ramis <ceb@zurich.ibm.com>
* chore(enricher): add defensive handling in '_validate_entities'
Add defensive handling in '_validate_entities()' to unwrap responses ensuring
validation consistency with the main parsing logic.
Signed-off-by: Cesar Berrospi Ramis <ceb@zurich.ibm.com>
* test: add regression tests for entity constraints
Signed-off-by: Cesar Berrospi Ramis <ceb@zurich.ibm.com>
---------
Signed-off-by: Ana Daniele <ana.daniele@ibm.com>
Signed-off-by: Cesar Berrospi Ramis <ceb@zurich.ibm.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Cesar Berrospi Ramis <ceb@zurich.ibm.com>1 parent b72d208 commit 33cd343
5 files changed
Lines changed: 498 additions & 14 deletions
File tree
- docling_agent/agent
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
860 | 860 | | |
861 | 861 | | |
862 | 862 | | |
| 863 | + | |
863 | 864 | | |
864 | | - | |
| 865 | + | |
865 | 866 | | |
866 | 867 | | |
867 | 868 | | |
| |||
880 | 881 | | |
881 | 882 | | |
882 | 883 | | |
883 | | - | |
| 884 | + | |
884 | 885 | | |
885 | | - | |
| 886 | + | |
| 887 | + | |
| 888 | + | |
| 889 | + | |
| 890 | + | |
| 891 | + | |
| 892 | + | |
| 893 | + | |
| 894 | + | |
| 895 | + | |
| 896 | + | |
886 | 897 | | |
887 | | - | |
888 | 898 | | |
889 | 899 | | |
890 | 900 | | |
| |||
923 | 933 | | |
924 | 934 | | |
925 | 935 | | |
926 | | - | |
927 | | - | |
| 936 | + | |
928 | 937 | | |
929 | 938 | | |
930 | 939 | | |
| |||
1027 | 1036 | | |
1028 | 1037 | | |
1029 | 1038 | | |
| 1039 | + | |
| 1040 | + | |
1030 | 1041 | | |
1031 | 1042 | | |
1032 | 1043 | | |
| |||
1035 | 1046 | | |
1036 | 1047 | | |
1037 | 1048 | | |
| 1049 | + | |
1038 | 1050 | | |
1039 | | - | |
| 1051 | + | |
1040 | 1052 | | |
1041 | 1053 | | |
1042 | 1054 | | |
1043 | | - | |
| 1055 | + | |
| 1056 | + | |
| 1057 | + | |
| 1058 | + | |
| 1059 | + | |
| 1060 | + | |
| 1061 | + | |
| 1062 | + | |
| 1063 | + | |
| 1064 | + | |
| 1065 | + | |
| 1066 | + | |
1044 | 1067 | | |
1045 | 1068 | | |
1046 | 1069 | | |
1047 | 1070 | | |
1048 | | - | |
1049 | 1071 | | |
1050 | 1072 | | |
1051 | 1073 | | |
| |||
1073 | 1095 | | |
1074 | 1096 | | |
1075 | 1097 | | |
1076 | | - | |
| 1098 | + | |
| 1099 | + | |
1077 | 1100 | | |
1078 | | - | |
| 1101 | + | |
| 1102 | + | |
| 1103 | + | |
| 1104 | + | |
| 1105 | + | |
| 1106 | + | |
1079 | 1107 | | |
| 1108 | + | |
1080 | 1109 | | |
1081 | 1110 | | |
1082 | 1111 | | |
1083 | 1112 | | |
| 1113 | + | |
| 1114 | + | |
| 1115 | + | |
| 1116 | + | |
| 1117 | + | |
| 1118 | + | |
| 1119 | + | |
1084 | 1120 | | |
1085 | 1121 | | |
1086 | 1122 | | |
| |||
1090 | 1126 | | |
1091 | 1127 | | |
1092 | 1128 | | |
| 1129 | + | |
| 1130 | + | |
| 1131 | + | |
| 1132 | + | |
| 1133 | + | |
| 1134 | + | |
1093 | 1135 | | |
1094 | 1136 | | |
1095 | 1137 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
205 | 205 | | |
206 | 206 | | |
207 | 207 | | |
| 208 | + | |
208 | 209 | | |
209 | 210 | | |
210 | 211 | | |
| |||
229 | 230 | | |
230 | 231 | | |
231 | 232 | | |
232 | | - | |
| 233 | + | |
233 | 234 | | |
234 | 235 | | |
235 | 236 | | |
| |||
278 | 279 | | |
279 | 280 | | |
280 | 281 | | |
281 | | - | |
| 282 | + | |
282 | 283 | | |
283 | 284 | | |
284 | 285 | | |
| |||
364 | 365 | | |
365 | 366 | | |
366 | 367 | | |
367 | | - | |
| 368 | + | |
368 | 369 | | |
369 | 370 | | |
370 | 371 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
0 commit comments