compass-kb-validation 2.2.0__tar.gz → 2.4.0__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.
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/PKG-INFO +5 -1
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/README.md +4 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/compass_kb_validation.egg-info/PKG-INFO +5 -1
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/compass_kb_validation.egg-info/SOURCES.txt +2 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/discover_and_validate.py +63 -8
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/__init__.py +1 -1
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/cli.py +69 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/config/__init__.py +34 -1
- compass_kb_validation-2.4.0/ingestion_validation/grounding.py +341 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/profiler.py +72 -1
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/providers/vectorstore.py +56 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/report/__init__.py +7 -2
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_config.py +25 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_discover_cli.py +57 -0
- compass_kb_validation-2.4.0/tests/test_grounding.py +150 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_reporting.py +15 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_vectorstore_filters.py +35 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/LICENSE +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/compass_kb_validation.egg-info/dependency_links.txt +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/compass_kb_validation.egg-info/entry_points.txt +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/compass_kb_validation.egg-info/requires.txt +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/compass_kb_validation.egg-info/top_level.txt +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/__main__.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/ask.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/baseline.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/chunk_coherence.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/config_lint.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/content_inspect.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/corpus.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/corpus_health.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/diagnostics.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/embedding_drift.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/eval_interop.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/init_wizard.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/live_dashboard.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/mcp_server.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/models/__init__.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/notifications.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/pipeline/__init__.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/provenance.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/providers/__init__.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/providers/embedding.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/providers/graph.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/providers/source.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/providers/storage.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/remediation.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/report/_report_css.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/report/_report_js.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/report/analysis.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/report/export.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/report/shell.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/retrieval_analysis.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/trend_tracker.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/utils/__init__.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/validators/__init__.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/validators/api_upload.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/validators/base.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/validators/base_sparql.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/validators/cross_stage.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/validators/graphdb_metadata.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/validators/neptune_metadata.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/validators/redis_chunks.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/validators/registry.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/validators/retrieval_quality.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/validators/s3_storage.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/profile_kb.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/pyproject.toml +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/run_validation.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/setup.cfg +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_alt_adapters.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_analysis_report.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_api_upload.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_ask.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_baseline.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_benchmark.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_check_catalog.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_cli.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_config_lint.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_content_inspect.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_corpus.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_corpus_health.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_coverage.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_cross_stage.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_diagnostics.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_discovery_parallel.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_embedding_drift.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_eval_interop.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_gate.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_graph_transport.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_graph_validators.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_init_wizard.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_manifest.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_mcp.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_migration.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_models.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_neptune_bugfixes.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_neptune_bugfixes2.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_notifications.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_pipeline.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_profiler.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_providers.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_redis_chunks.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_registry.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_remediation.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_retrieval_assertions.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_retrieval_quality.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_run_cli.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_sources.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_storage_validator.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_trends.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_universal_concerns.py +0 -0
- {compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: compass-kb-validation
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: Knowledge-base readiness validation for RAG & AI agents — config-driven, deterministic pipeline integrity checks.
|
|
5
5
|
Author: COMPASS
|
|
6
6
|
License: MIT
|
|
@@ -228,6 +228,10 @@ compass ask --project my-kb --batch questions.jsonl --format eval --output eval_
|
|
|
228
228
|
# Corpus health (deterministic, no LLM): score chunk quality / find stale + orphan sources
|
|
229
229
|
compass quality --project my-kb
|
|
230
230
|
compass freshness --project my-kb --stale-after 30
|
|
231
|
+
|
|
232
|
+
# Adjudicate an eval's grounding finding against the KB: did the bot's counts match reality?
|
|
233
|
+
# Deterministic - attributes a failure to a layer (retrieval vs the model), never scores answer quality.
|
|
234
|
+
compass grounding-audit --project my-kb --eval-result case.json --fail-on-fabrication
|
|
231
235
|
```
|
|
232
236
|
|
|
233
237
|
`migrate` and `coverage` also render a **self-contained HTML report** (same look as the
|
|
@@ -161,6 +161,10 @@ compass ask --project my-kb --batch questions.jsonl --format eval --output eval_
|
|
|
161
161
|
# Corpus health (deterministic, no LLM): score chunk quality / find stale + orphan sources
|
|
162
162
|
compass quality --project my-kb
|
|
163
163
|
compass freshness --project my-kb --stale-after 30
|
|
164
|
+
|
|
165
|
+
# Adjudicate an eval's grounding finding against the KB: did the bot's counts match reality?
|
|
166
|
+
# Deterministic - attributes a failure to a layer (retrieval vs the model), never scores answer quality.
|
|
167
|
+
compass grounding-audit --project my-kb --eval-result case.json --fail-on-fabrication
|
|
164
168
|
```
|
|
165
169
|
|
|
166
170
|
`migrate` and `coverage` also render a **self-contained HTML report** (same look as the
|
{compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/compass_kb_validation.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: compass-kb-validation
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: Knowledge-base readiness validation for RAG & AI agents — config-driven, deterministic pipeline integrity checks.
|
|
5
5
|
Author: COMPASS
|
|
6
6
|
License: MIT
|
|
@@ -228,6 +228,10 @@ compass ask --project my-kb --batch questions.jsonl --format eval --output eval_
|
|
|
228
228
|
# Corpus health (deterministic, no LLM): score chunk quality / find stale + orphan sources
|
|
229
229
|
compass quality --project my-kb
|
|
230
230
|
compass freshness --project my-kb --stale-after 30
|
|
231
|
+
|
|
232
|
+
# Adjudicate an eval's grounding finding against the KB: did the bot's counts match reality?
|
|
233
|
+
# Deterministic - attributes a failure to a layer (retrieval vs the model), never scores answer quality.
|
|
234
|
+
compass grounding-audit --project my-kb --eval-result case.json --fail-on-fabrication
|
|
231
235
|
```
|
|
232
236
|
|
|
233
237
|
`migrate` and `coverage` also render a **self-contained HTML report** (same look as the
|
|
@@ -23,6 +23,7 @@ ingestion_validation/corpus_health.py
|
|
|
23
23
|
ingestion_validation/diagnostics.py
|
|
24
24
|
ingestion_validation/embedding_drift.py
|
|
25
25
|
ingestion_validation/eval_interop.py
|
|
26
|
+
ingestion_validation/grounding.py
|
|
26
27
|
ingestion_validation/init_wizard.py
|
|
27
28
|
ingestion_validation/live_dashboard.py
|
|
28
29
|
ingestion_validation/mcp_server.py
|
|
@@ -82,6 +83,7 @@ tests/test_eval_interop.py
|
|
|
82
83
|
tests/test_gate.py
|
|
83
84
|
tests/test_graph_transport.py
|
|
84
85
|
tests/test_graph_validators.py
|
|
86
|
+
tests/test_grounding.py
|
|
85
87
|
tests/test_init_wizard.py
|
|
86
88
|
tests/test_manifest.py
|
|
87
89
|
tests/test_mcp.py
|
|
@@ -67,6 +67,12 @@ def parse_args(argv=None) -> argparse.Namespace:
|
|
|
67
67
|
help="Also fail unless the KB Hygiene score is >= PCT (0-100). Hygiene is "
|
|
68
68
|
"the share of advisory data-quality checks that passed - data-plane "
|
|
69
69
|
"cleanliness, separate from the READY integrity gate.")
|
|
70
|
+
p.add_argument("--suggest-config", nargs="?", const="-", metavar="PATH",
|
|
71
|
+
help="After the run, emit config refinements from what it observed "
|
|
72
|
+
"(document count, chunk-size distribution, checks that never applied). "
|
|
73
|
+
"Prints to stdout, or writes to PATH. Review before merging.")
|
|
74
|
+
p.add_argument("--no-tips", action="store_true",
|
|
75
|
+
help="Suppress the end-of-run tips about relevant unused flags.")
|
|
70
76
|
# --- config overrides (run scenarios without editing YAML; CLI wins over config) ---
|
|
71
77
|
p.add_argument("--readiness-threshold", type=float, metavar="FRAC",
|
|
72
78
|
help="Override corpus readiness cutoff (0-1). READY when pass-rate >= this.")
|
|
@@ -136,8 +142,8 @@ def _resolve_skip_steps(settings, *, include_graphdb: bool, include_retrieval: b
|
|
|
136
142
|
skip += ["GraphDB Metadata", "Neptune Metadata"]
|
|
137
143
|
if not include_retrieval:
|
|
138
144
|
skip.append("Retrieval Quality")
|
|
139
|
-
skip += _normalize_skips(list(getattr(settings, "skip_steps", None) or []))
|
|
140
|
-
skip += _normalize_skips(cli_skip)
|
|
145
|
+
skip += _normalize_skips(list(getattr(settings, "skip_steps", None) or []), source="config")
|
|
146
|
+
skip += _normalize_skips(cli_skip, source="cli")
|
|
141
147
|
|
|
142
148
|
s3_auto_skipped = False
|
|
143
149
|
if settings.discovery.provider in ("vectorstore", "index", "manifest"):
|
|
@@ -149,8 +155,14 @@ def _resolve_skip_steps(settings, *, include_graphdb: bool, include_retrieval: b
|
|
|
149
155
|
return list(dict.fromkeys(skip)), s3_auto_skipped
|
|
150
156
|
|
|
151
157
|
|
|
152
|
-
def _normalize_skips(tokens: list[str]) -> list[str]:
|
|
153
|
-
"""Map --skip tokens (step keys or names) to the step names the pipeline skips by.
|
|
158
|
+
def _normalize_skips(tokens: list[str], source: str = "cli") -> list[str]:
|
|
159
|
+
"""Map --skip tokens (step keys or names) to the step names the pipeline skips by.
|
|
160
|
+
|
|
161
|
+
An unknown entry from an interactive ``--skip`` is worth flagging (the user just typed
|
|
162
|
+
it and expects it to work). An unknown entry from the project's ``skip_steps`` is
|
|
163
|
+
config drift (e.g. a step removed in an upgrade) - silent at runtime because it is
|
|
164
|
+
harmless there and firing every run is noise; ``compass lint`` surfaces it once.
|
|
165
|
+
"""
|
|
154
166
|
from ingestion_validation.validators.registry import registered_steps
|
|
155
167
|
specs = registered_steps()
|
|
156
168
|
by_key = {s.key: s.name for s in specs}
|
|
@@ -161,7 +173,7 @@ def _normalize_skips(tokens: list[str]) -> list[str]:
|
|
|
161
173
|
out.append(by_key[t])
|
|
162
174
|
elif t in names:
|
|
163
175
|
out.append(t)
|
|
164
|
-
|
|
176
|
+
elif source == "cli":
|
|
165
177
|
print(f" (skip: unknown step '{t}'; valid keys: {', '.join(sorted(by_key))})")
|
|
166
178
|
return out
|
|
167
179
|
|
|
@@ -240,6 +252,32 @@ _AUTH_HINTS = ("403", "forbidden", "expired", "credential", "token", "signatured
|
|
|
240
252
|
"invalidclienttokenid", "accessdenied", "unauthor")
|
|
241
253
|
|
|
242
254
|
|
|
255
|
+
def _print_tips(args, hygiene: dict | None, saved_baseline: bool) -> None:
|
|
256
|
+
"""End-of-run hints about the most relevant unused flags.
|
|
257
|
+
|
|
258
|
+
Discoverability, not noise: at most three, only when the flag was NOT already used and
|
|
259
|
+
the run's own outcome makes it relevant. Suppress with --no-tips. This is why a partner
|
|
260
|
+
can run for weeks without finding --save-baseline / --unreachable-as / --suggest-config.
|
|
261
|
+
"""
|
|
262
|
+
tips: list[str] = []
|
|
263
|
+
if not saved_baseline:
|
|
264
|
+
tips.append("Save this run as a baseline to catch regressions next time: "
|
|
265
|
+
"--save-baseline baselines/{}.json".format(args.project or "kb"))
|
|
266
|
+
if hygiene and hygiene.get("score", 100) < 90 and args.quality_threshold is None:
|
|
267
|
+
tips.append("Gate CI on data-plane hygiene (not just integrity): "
|
|
268
|
+
"--quality-threshold {}".format(int(hygiene["score"])))
|
|
269
|
+
if not getattr(args, "include_retrieval", False):
|
|
270
|
+
tips.append("Add retrieval-quality checks (can a stored vector be found via KNN?): "
|
|
271
|
+
"--include-retrieval")
|
|
272
|
+
if not args.suggest_config:
|
|
273
|
+
tips.append("Refine your config from what this run observed: --suggest-config")
|
|
274
|
+
if not tips:
|
|
275
|
+
return
|
|
276
|
+
print("\n Tips (--no-tips to silence):")
|
|
277
|
+
for t in tips[:3]:
|
|
278
|
+
print(f" * {t}")
|
|
279
|
+
|
|
280
|
+
|
|
243
281
|
def _resolve_expected_documents(raw, project: str):
|
|
244
282
|
"""Resolve the configured expected_documents, supporting ``auto``.
|
|
245
283
|
|
|
@@ -476,13 +514,18 @@ def main(argv=None) -> int:
|
|
|
476
514
|
# Corpus-level knowledge-base readiness (completeness, dedup, coverage).
|
|
477
515
|
from ingestion_validation.corpus import analyze_corpus
|
|
478
516
|
|
|
517
|
+
# Read corpus expectations from the canonical `corpus:` block, falling back to the
|
|
518
|
+
# legacy `discovery.extra` location so older configs keep working.
|
|
519
|
+
cc = settings.corpus
|
|
520
|
+
dx = settings.discovery.extra
|
|
479
521
|
expected_documents = _resolve_expected_documents(
|
|
480
|
-
|
|
522
|
+
cc.expected_documents if cc.expected_documents is not None else dx.get("expected_documents"),
|
|
523
|
+
args.project)
|
|
481
524
|
corpus = analyze_corpus(
|
|
482
525
|
documents, results,
|
|
483
526
|
expected_documents=expected_documents,
|
|
484
|
-
expected_folders=
|
|
485
|
-
expected_document_names=
|
|
527
|
+
expected_folders=cc.expected_folders or dx.get("expected_folders"),
|
|
528
|
+
expected_document_names=cc.expected_document_names or dx.get("expected_document_names"),
|
|
486
529
|
readiness_threshold=settings.pipeline.readiness_threshold,
|
|
487
530
|
)
|
|
488
531
|
print(f" KB readiness: {corpus['verdict']} ({corpus['passed']}/{corpus['total']} passed, "
|
|
@@ -502,6 +545,16 @@ def main(argv=None) -> int:
|
|
|
502
545
|
if args.output_format in ("junit", "all"):
|
|
503
546
|
print(f" JUnit report: {export_junit(results, base + '.xml')}")
|
|
504
547
|
|
|
548
|
+
if args.suggest_config:
|
|
549
|
+
from ingestion_validation.profiler import suggest_config_from_run
|
|
550
|
+
snippet = suggest_config_from_run(results, documents)
|
|
551
|
+
if args.suggest_config == "-":
|
|
552
|
+
print("\n" + snippet)
|
|
553
|
+
else:
|
|
554
|
+
from pathlib import Path as _P
|
|
555
|
+
_P(args.suggest_config).write_text(snippet, encoding="utf-8")
|
|
556
|
+
print(f" Config suggestions written: {args.suggest_config} (review before merging)")
|
|
557
|
+
|
|
505
558
|
# KB Hygiene: data-plane cleanliness (advisory checks), separate from the integrity
|
|
506
559
|
# gate. Printed for every run; gates the build only when --quality-threshold is set.
|
|
507
560
|
# Best-effort context - a failure to score it must not crash the gate decision.
|
|
@@ -539,6 +592,8 @@ def main(argv=None) -> int:
|
|
|
539
592
|
reason = f"{len(documents)} documents < s3.min_documents ({min_docs})"
|
|
540
593
|
failed_gate = True
|
|
541
594
|
code = EXIT_GATE_FAILED if (failed_gate or (regression and args.fail_on_regression)) else EXIT_OK
|
|
595
|
+
if not args.no_tips:
|
|
596
|
+
_print_tips(args, hygiene, saved_baseline=args.save_baseline is not None or bool(args.against))
|
|
542
597
|
print("=" * 60)
|
|
543
598
|
print(f" Results: {passed} passed, {failed} failed ({len(results)} total)")
|
|
544
599
|
print(f" Gate: {'FAILED' if failed_gate else 'PASSED'} - {reason}")
|
|
@@ -1302,6 +1302,72 @@ def cmd_freshness(argv: list[str]) -> int:
|
|
|
1302
1302
|
return 0
|
|
1303
1303
|
|
|
1304
1304
|
|
|
1305
|
+
def cmd_grounding_audit(argv: list[str]) -> int:
|
|
1306
|
+
p = argparse.ArgumentParser(
|
|
1307
|
+
prog="compass grounding-audit",
|
|
1308
|
+
description="Adjudicate an eval's grounding finding against the live KB. Deterministic: "
|
|
1309
|
+
"verifies count/aggregation claims exactly against the KB and attributes the "
|
|
1310
|
+
"failure to a layer (retrieval vs the model). Does not judge answer quality.")
|
|
1311
|
+
p.add_argument("--project")
|
|
1312
|
+
p.add_argument("--env", default="")
|
|
1313
|
+
g = p.add_mutually_exclusive_group(required=True)
|
|
1314
|
+
g.add_argument("--eval-result", metavar="FILE", help="One eval record as JSON.")
|
|
1315
|
+
g.add_argument("--eval-results", metavar="FILE", help="JSONL of eval records (one per line).")
|
|
1316
|
+
p.add_argument("--format", choices=["markdown", "json"], default="markdown")
|
|
1317
|
+
p.add_argument("--output", help="Write the report to a file (default: stdout).")
|
|
1318
|
+
p.add_argument("--fail-on-fabrication", action="store_true",
|
|
1319
|
+
help="Exit non-zero if any verifiable claim disagrees with the KB.")
|
|
1320
|
+
args = p.parse_args(argv)
|
|
1321
|
+
_health_env(args)
|
|
1322
|
+
|
|
1323
|
+
import json as _json
|
|
1324
|
+
|
|
1325
|
+
from ingestion_validation.config import load_settings, project_config_file
|
|
1326
|
+
from ingestion_validation.grounding import render_report, run_audit
|
|
1327
|
+
from ingestion_validation.providers import get_adapter
|
|
1328
|
+
|
|
1329
|
+
if args.project and project_config_file(args.project) is None:
|
|
1330
|
+
print(f" Project '{args.project}' not found (config/projects/{args.project}.yaml).")
|
|
1331
|
+
return 2
|
|
1332
|
+
settings = load_settings(project=args.project, env=args.env or None)
|
|
1333
|
+
|
|
1334
|
+
# Load the eval record(s).
|
|
1335
|
+
records: list[dict] = []
|
|
1336
|
+
try:
|
|
1337
|
+
if args.eval_result:
|
|
1338
|
+
records = [_json.loads(Path(args.eval_result).read_text(encoding="utf-8"))]
|
|
1339
|
+
else:
|
|
1340
|
+
for line in Path(args.eval_results).read_text(encoding="utf-8").splitlines():
|
|
1341
|
+
if line.strip():
|
|
1342
|
+
records.append(_json.loads(line))
|
|
1343
|
+
except Exception as exc: # noqa: BLE001 - clean message, never a traceback
|
|
1344
|
+
print(f" Could not read eval input: {exc}")
|
|
1345
|
+
return 2
|
|
1346
|
+
|
|
1347
|
+
adapter = get_adapter("vectorstore", settings.redis.provider, config=settings.redis)
|
|
1348
|
+
results = [run_audit(rec, adapter, settings.eval) for rec in records]
|
|
1349
|
+
|
|
1350
|
+
if args.format == "json":
|
|
1351
|
+
from dataclasses import asdict
|
|
1352
|
+
body = _json.dumps([asdict(r) for r in results], indent=2, default=str)
|
|
1353
|
+
else:
|
|
1354
|
+
body = ("\n\n".join(render_report(r, project=args.project or "") for r in results))
|
|
1355
|
+
|
|
1356
|
+
if args.output:
|
|
1357
|
+
Path(args.output).parent.mkdir(parents=True, exist_ok=True)
|
|
1358
|
+
Path(args.output).write_text(body + "\n", encoding="utf-8")
|
|
1359
|
+
print(f" Wrote {args.output}")
|
|
1360
|
+
else:
|
|
1361
|
+
print(body)
|
|
1362
|
+
|
|
1363
|
+
fabricated = sum(1 for r in results if r.wrong_claims)
|
|
1364
|
+
if fabricated:
|
|
1365
|
+
print(f"\n {fabricated}/{len(results)} case(s) contain claims that disagree with the KB.")
|
|
1366
|
+
if args.fail_on_fabrication:
|
|
1367
|
+
return 1
|
|
1368
|
+
return 0
|
|
1369
|
+
|
|
1370
|
+
|
|
1305
1371
|
# --------------------------------------------------------------------------- #
|
|
1306
1372
|
# compass selftest - prove exit-code propagation in THIS shell (BUG-14/15 diagnosis)
|
|
1307
1373
|
# --------------------------------------------------------------------------- #
|
|
@@ -1355,6 +1421,7 @@ Commands:
|
|
|
1355
1421
|
compare compare retrieval against a ground-truth file (precision/recall/Jaccard)
|
|
1356
1422
|
quality score chunk quality (density, self-containedness, boilerplate) - no LLM
|
|
1357
1423
|
freshness flag stale sources + orphan documents (source object gone)
|
|
1424
|
+
grounding-audit check an eval's grounding finding against the KB (count claims) - no LLM
|
|
1358
1425
|
demo generate a sample report from synthetic data (no backend needed)
|
|
1359
1426
|
init [name] scaffold a project (config + .env.example + marker). -i/--interactive wizard; --graph graph block; --ci workflow
|
|
1360
1427
|
lint statically check a project config for silent-failure traps (no backend)
|
|
@@ -1399,6 +1466,8 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1399
1466
|
return cmd_compare(rest)
|
|
1400
1467
|
if cmd == "quality":
|
|
1401
1468
|
return cmd_quality(rest)
|
|
1469
|
+
if cmd == "grounding-audit":
|
|
1470
|
+
return cmd_grounding_audit(rest)
|
|
1402
1471
|
if cmd == "freshness":
|
|
1403
1472
|
return cmd_freshness(rest)
|
|
1404
1473
|
if cmd == "selftest":
|
{compass_kb_validation-2.2.0 → compass_kb_validation-2.4.0}/ingestion_validation/config/__init__.py
RENAMED
|
@@ -534,6 +534,22 @@ class DiscoveryConfig:
|
|
|
534
534
|
extra: dict[str, Any] = field(default_factory=dict)
|
|
535
535
|
|
|
536
536
|
|
|
537
|
+
@dataclass
|
|
538
|
+
class CorpusConfig:
|
|
539
|
+
"""Corpus-level expectations: what SHOULD be in the knowledge base.
|
|
540
|
+
|
|
541
|
+
The natural home for the expected document set, so completeness alerts on drift. This
|
|
542
|
+
is the block users reach for (`corpus:`); previously it was neither recognized nor read
|
|
543
|
+
(the values had to live under `discovery.extra`, which few discover), so a
|
|
544
|
+
`corpus.expected_documents` was silently ignored. Now first-class and read directly.
|
|
545
|
+
"""
|
|
546
|
+
|
|
547
|
+
expected_documents: Any = None # int, or "auto" (baseline from last run)
|
|
548
|
+
expected_folders: list[str] = field(default_factory=list)
|
|
549
|
+
expected_document_names: list[str] = field(default_factory=list)
|
|
550
|
+
extra: dict[str, Any] = field(default_factory=dict)
|
|
551
|
+
|
|
552
|
+
|
|
537
553
|
@dataclass
|
|
538
554
|
class EmbeddingConfig:
|
|
539
555
|
"""How to turn a natural-language query into a vector for `compass ask`.
|
|
@@ -582,6 +598,21 @@ class EvalConfig:
|
|
|
582
598
|
gt_variant_path: str = ""
|
|
583
599
|
gt_variant_filter: list[str] = field(default_factory=list)
|
|
584
600
|
|
|
601
|
+
# --- Grounding audit (compass grounding-audit) ------------------------------------
|
|
602
|
+
# Verify an eval's grounding finding against the live KB. Every field name here comes
|
|
603
|
+
# from config, never assumed - a KB whose ingestion named things differently only
|
|
604
|
+
# changes these values, not code.
|
|
605
|
+
gt_response_path: str = "response" # the answer text in the eval record
|
|
606
|
+
gt_context_path: str = "context.knowledge_retrieval" # the retrieved context the eval saw
|
|
607
|
+
gt_scores_path: str = "scores" # eval scores (shown in the report)
|
|
608
|
+
# Structured-query verification: which fields the KB can aggregate to check a count
|
|
609
|
+
# claim. Each entry: {field: <group-by field>, distinct_by: <doc-identity field>,
|
|
610
|
+
# label: <optional human label>}. Empty = count claims are not verified.
|
|
611
|
+
aggregatable_fields: list[dict] = field(default_factory=list)
|
|
612
|
+
# Extra query-intent regexes, merged with the built-in defaults (how many / count / by
|
|
613
|
+
# type / list all / most recent ...). Lets a domain add its own phrasings.
|
|
614
|
+
structured_query_patterns: list[str] = field(default_factory=list)
|
|
615
|
+
|
|
585
616
|
|
|
586
617
|
@dataclass
|
|
587
618
|
class ConsistencyConfig:
|
|
@@ -640,6 +671,7 @@ class Settings:
|
|
|
640
671
|
embedding: EmbeddingConfig = field(default_factory=EmbeddingConfig)
|
|
641
672
|
eval: EvalConfig = field(default_factory=EvalConfig)
|
|
642
673
|
consistency: ConsistencyConfig = field(default_factory=ConsistencyConfig)
|
|
674
|
+
corpus: CorpusConfig = field(default_factory=CorpusConfig)
|
|
643
675
|
# Non-service metadata carried through from config files.
|
|
644
676
|
project_name: str = ""
|
|
645
677
|
display_name: str = ""
|
|
@@ -661,7 +693,7 @@ _META_KEYS = frozenset({"default_env", "display_name", "environments", "skip_ste
|
|
|
661
693
|
# new block can never be added to one and forgotten in the other (which is exactly how
|
|
662
694
|
# `consistency` came to be silently dropped at the top level).
|
|
663
695
|
_SERVICE_BLOCKS = ("api", "s3", "graphdb", "redis", "neptune", "pipeline",
|
|
664
|
-
"retrieval", "discovery", "embedding", "eval", "consistency")
|
|
696
|
+
"retrieval", "discovery", "embedding", "eval", "consistency", "corpus")
|
|
665
697
|
|
|
666
698
|
|
|
667
699
|
def _deep_merge(base: dict, overlay: dict) -> dict:
|
|
@@ -787,6 +819,7 @@ def load_settings(
|
|
|
787
819
|
embedding=_hydrate(EmbeddingConfig, flat.get("embedding")),
|
|
788
820
|
eval=_hydrate(EvalConfig, flat.get("eval")),
|
|
789
821
|
consistency=_hydrate(ConsistencyConfig, flat.get("consistency")),
|
|
822
|
+
corpus=_hydrate(CorpusConfig, flat.get("corpus")),
|
|
790
823
|
project_name=project or "",
|
|
791
824
|
display_name=merged.get("display_name", ""),
|
|
792
825
|
environment=resolved_env,
|