ski-kg-validator 3.0.3__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.
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.4
2
+ Name: ski-kg-validator
3
+ Version: 3.0.3
4
+ Summary: Validate and review extracted compliance rules with mandatory human expert oversight.
5
+ Author-email: KpiFinity <hello@kpifinity.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/kpifinity/ski-framework
8
+ Project-URL: Documentation, https://kpifinity.github.io/ski-framework/
9
+ Project-URL: Issues, https://github.com/kpifinity/ski-framework/issues
10
+ Keywords: ski-framework,compliance,knowledge-graph,validation
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Legal Industry
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: pydantic<3,>=2.13.4
23
+ Requires-Dist: click<9,>=8.1.7
24
+ Requires-Dist: python-dotenv<2,>=1.2.2
25
+ Requires-Dist: rapidfuzz<4,>=3.7
26
+ Requires-Dist: jinja2<4,>=3.1.6
27
+
28
+ # kg-validator
29
+
30
+ > Install from PyPI: `pip install ski-kg-validator` (publishing starts with the first release after June 2026).
31
+
32
+ > **Status:** v3.0 — first production-target release.
33
+
34
+ Validate v3 Knowledge Graphs against SKI Framework specification v3.0
35
+ §3 (schema) and §3.6 (cross-cutting validation passes). Phase 1
36
+ (compilation) tool.
37
+
38
+ ## What it does
39
+
40
+ - **Schema validation.** Parses a v3 KG JSON file into the typed-node
41
+ Pydantic models. Unknown obligation types, unknown edge types,
42
+ unknown risk tiers, missing required fields, and extra unknown keys
43
+ are all rejected at load time.
44
+ - **Cross-cutting validation (§3.6).** Detects duplicate node IDs,
45
+ dangling edges, edges pointing at the wrong target node type, rules
46
+ without any obligation, and orphan obligations.
47
+
48
+ ## What it no longer does (v2 retired in PR 10e)
49
+
50
+ - The v2 flat-rule-list format is gone. v3 typed obligations are the
51
+ only currency.
52
+ - Interactive `review`, `detect-conflicts`, `detect-duplicates`, and
53
+ HTML `report` subcommands were retired with the v2 path — they
54
+ worked on the flat-rule shape and have no v3 equivalent. v3
55
+ conflict and duplicate detection happen at the typed-obligation
56
+ level inside the validation passes and surface as issues in the
57
+ output JSON.
58
+ - The `ConfidenceLevel` enum is gone. The extractor now carries an
59
+ `ExtractionQuality` value (separate concept; see `kg-extractor`).
60
+
61
+ ## Installation
62
+
63
+ ```bash
64
+ pip install -e tools/kg-validator
65
+ ```
66
+
67
+ ## Quick start
68
+
69
+ ```bash
70
+ # Validate a v3 KG and print the issue summary:
71
+ kg-validator validate --input examples/energy/knowledge-graphs/kg-energy-v3-demo.json
72
+
73
+ # Validate and persist the full issue report:
74
+ kg-validator validate \
75
+ --input kg-energy-v3.json \
76
+ --output validation-report.json
77
+
78
+ # Show usage examples:
79
+ kg-validator examples
80
+
81
+ # Show version:
82
+ kg-validator version
83
+ ```
84
+
85
+ The `validate` command exits with code 0 on a clean KG, code 2 on a
86
+ schema-level Pydantic validation failure, and code 3 on any CRITICAL
87
+ or HIGH severity §3.6 issue.
88
+
89
+ ## Architecture
90
+
91
+ ```
92
+ kg_validator/
93
+ ├── __init__.py public API: KnowledgeGraphV3, V3Validator, load_v3_kg, models
94
+ ├── loader.py KnowledgeGraphV3 container + load_v3_kg(path) entry point
95
+ ├── models.py typed Pydantic models per spec v3.0 §3 (nodes, edges, enums)
96
+ ├── validator.py V3Validator runs the §3.6 cross-cutting passes
97
+ └── cli.py click-based CLI (`validate`, `examples`, `version`)
98
+ ```
99
+
100
+ ## Conformance
101
+
102
+ The `examples/energy/knowledge-graphs/kg-energy-v3-demo.json` sample
103
+ KG must load cleanly and produce zero validation issues. This is
104
+ exercised by `tests/test_v3_validator.py` and by the CI durability
105
+ suite.
@@ -0,0 +1,78 @@
1
+ # kg-validator
2
+
3
+ > Install from PyPI: `pip install ski-kg-validator` (publishing starts with the first release after June 2026).
4
+
5
+ > **Status:** v3.0 — first production-target release.
6
+
7
+ Validate v3 Knowledge Graphs against SKI Framework specification v3.0
8
+ §3 (schema) and §3.6 (cross-cutting validation passes). Phase 1
9
+ (compilation) tool.
10
+
11
+ ## What it does
12
+
13
+ - **Schema validation.** Parses a v3 KG JSON file into the typed-node
14
+ Pydantic models. Unknown obligation types, unknown edge types,
15
+ unknown risk tiers, missing required fields, and extra unknown keys
16
+ are all rejected at load time.
17
+ - **Cross-cutting validation (§3.6).** Detects duplicate node IDs,
18
+ dangling edges, edges pointing at the wrong target node type, rules
19
+ without any obligation, and orphan obligations.
20
+
21
+ ## What it no longer does (v2 retired in PR 10e)
22
+
23
+ - The v2 flat-rule-list format is gone. v3 typed obligations are the
24
+ only currency.
25
+ - Interactive `review`, `detect-conflicts`, `detect-duplicates`, and
26
+ HTML `report` subcommands were retired with the v2 path — they
27
+ worked on the flat-rule shape and have no v3 equivalent. v3
28
+ conflict and duplicate detection happen at the typed-obligation
29
+ level inside the validation passes and surface as issues in the
30
+ output JSON.
31
+ - The `ConfidenceLevel` enum is gone. The extractor now carries an
32
+ `ExtractionQuality` value (separate concept; see `kg-extractor`).
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install -e tools/kg-validator
38
+ ```
39
+
40
+ ## Quick start
41
+
42
+ ```bash
43
+ # Validate a v3 KG and print the issue summary:
44
+ kg-validator validate --input examples/energy/knowledge-graphs/kg-energy-v3-demo.json
45
+
46
+ # Validate and persist the full issue report:
47
+ kg-validator validate \
48
+ --input kg-energy-v3.json \
49
+ --output validation-report.json
50
+
51
+ # Show usage examples:
52
+ kg-validator examples
53
+
54
+ # Show version:
55
+ kg-validator version
56
+ ```
57
+
58
+ The `validate` command exits with code 0 on a clean KG, code 2 on a
59
+ schema-level Pydantic validation failure, and code 3 on any CRITICAL
60
+ or HIGH severity §3.6 issue.
61
+
62
+ ## Architecture
63
+
64
+ ```
65
+ kg_validator/
66
+ ├── __init__.py public API: KnowledgeGraphV3, V3Validator, load_v3_kg, models
67
+ ├── loader.py KnowledgeGraphV3 container + load_v3_kg(path) entry point
68
+ ├── models.py typed Pydantic models per spec v3.0 §3 (nodes, edges, enums)
69
+ ├── validator.py V3Validator runs the §3.6 cross-cutting passes
70
+ └── cli.py click-based CLI (`validate`, `examples`, `version`)
71
+ ```
72
+
73
+ ## Conformance
74
+
75
+ The `examples/energy/knowledge-graphs/kg-energy-v3-demo.json` sample
76
+ KG must load cleanly and produce zero validation issues. This is
77
+ exercised by `tests/test_v3_validator.py` and by the CI durability
78
+ suite.
@@ -0,0 +1,45 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ski-kg-validator"
7
+ version = "3.0.3"
8
+ description = "Validate and review extracted compliance rules with mandatory human expert oversight."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "Apache-2.0"
12
+ authors = [{ name = "KpiFinity", email = "hello@kpifinity.com" }]
13
+ keywords = ["ski-framework", "compliance", "knowledge-graph", "validation"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "Intended Audience :: Legal Industry",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Topic :: Software Development :: Libraries :: Python Modules",
24
+ ]
25
+ dependencies = [
26
+ "pydantic>=2.13.4,<3",
27
+ "click>=8.1.7,<9",
28
+ "python-dotenv>=1.2.2,<2",
29
+ "rapidfuzz>=3.7,<4",
30
+ "jinja2>=3.1.6,<4",
31
+ ]
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/kpifinity/ski-framework"
35
+ Documentation = "https://kpifinity.github.io/ski-framework/"
36
+ Issues = "https://github.com/kpifinity/ski-framework/issues"
37
+
38
+ [project.scripts]
39
+ kg-validator = "kg_validator.cli:main"
40
+
41
+ [tool.setuptools.packages.find]
42
+ where = ["src"]
43
+
44
+ [tool.setuptools.package-data]
45
+ kg_validator = ["py.typed"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,45 @@
1
+ """kg-validator — schema and §3.6 cross-cutting validation for v3 KGs."""
2
+
3
+ from .loader import KnowledgeGraphV3, load_v3_kg
4
+ from .models import (
5
+ Citation,
6
+ Definition,
7
+ Edge,
8
+ EdgeType,
9
+ Exemption,
10
+ Jurisdiction,
11
+ Obligation,
12
+ ObligationType,
13
+ Precedent,
14
+ RiskTier,
15
+ Rule,
16
+ Subject,
17
+ V3IssueType,
18
+ V3ValidationIssue,
19
+ V3ValidationResult,
20
+ )
21
+ from .validator import V3Validator
22
+
23
+ __version__ = "3.0.3"
24
+ __author__ = "KpiFinity"
25
+
26
+ __all__ = [
27
+ "Citation",
28
+ "Definition",
29
+ "Edge",
30
+ "EdgeType",
31
+ "Exemption",
32
+ "Jurisdiction",
33
+ "KnowledgeGraphV3",
34
+ "Obligation",
35
+ "ObligationType",
36
+ "Precedent",
37
+ "RiskTier",
38
+ "Rule",
39
+ "Subject",
40
+ "V3IssueType",
41
+ "V3ValidationIssue",
42
+ "V3ValidationResult",
43
+ "V3Validator",
44
+ "load_v3_kg",
45
+ ]
@@ -0,0 +1,111 @@
1
+ """Command-line interface for kg-validator (v3-only).
2
+
3
+ The validator now accepts a single subcommand — ``validate`` — which
4
+ loads a v3 KG JSON file and runs the spec §3.6 validation passes.
5
+
6
+ The v2 ``review``, ``detect-conflicts``, ``detect-duplicates``, and
7
+ HTML ``report`` subcommands were retired with PR 10e: they targeted
8
+ the flat-rule list shape that is no longer the framework's currency.
9
+ v3 conflict/duplicate detection happens at the typed-obligation level
10
+ inside :mod:`kg_validator.validator` and surfaces as issues in the
11
+ :class:`V3ValidationResult`.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import json
17
+ from typing import Optional
18
+
19
+ import click
20
+ from pydantic import ValidationError
21
+
22
+ from . import __version__
23
+ from .loader import load_v3_kg
24
+ from .validator import V3Validator
25
+
26
+
27
+ @click.group()
28
+ def main() -> None:
29
+ """Validate v3 KG files against SKI Framework spec v3.0 §3."""
30
+
31
+
32
+ @main.command()
33
+ @click.option("--input", "-i", required=True, help="Input v3 KG JSON file.")
34
+ @click.option(
35
+ "--output",
36
+ "-o",
37
+ default=None,
38
+ help="Output JSON file with validation results. If omitted, results are printed to stdout when issues are found.",
39
+ )
40
+ def validate(input: str, output: Optional[str]) -> None:
41
+ """Validate a v3 KG against spec §3 schema and §3.6 cross-cutting passes."""
42
+ try:
43
+ click.echo(f"Loading v3 KG from: {input}")
44
+ kg = load_v3_kg(input)
45
+ except FileNotFoundError as e:
46
+ click.echo(f"Error: {e!s}", err=True)
47
+ raise SystemExit(1) from e
48
+ except ValidationError as e:
49
+ click.echo("Schema validation failed:", err=True)
50
+ click.echo(str(e), err=True)
51
+ raise SystemExit(2) from e
52
+
53
+ click.echo(
54
+ f"Loaded {len(kg.nodes.rules)} rules, {len(kg.nodes.obligations)} obligations, {len(kg.edges)} edges"
55
+ )
56
+
57
+ result = V3Validator(kg).run()
58
+
59
+ click.echo("\nValidation complete:")
60
+ click.echo(f" Nodes: {result.total_nodes}")
61
+ click.echo(f" Edges: {result.total_edges}")
62
+ click.echo(f" Issues: {result.total_issues}")
63
+ if result.total_issues:
64
+ click.echo("\nIssues by type:")
65
+ by_type: dict[str, int] = {}
66
+ for issue in result.issues:
67
+ key = str(issue.issue_type)
68
+ by_type[key] = by_type.get(key, 0) + 1
69
+ for key in sorted(by_type):
70
+ click.echo(f" {key}: {by_type[key]}")
71
+
72
+ payload = result.model_dump(mode="json")
73
+ if output:
74
+ with open(output, "w", encoding="utf-8") as fh:
75
+ json.dump(payload, fh, indent=2)
76
+ click.echo(f"\nResults saved to: {output}")
77
+ elif result.total_issues:
78
+ click.echo("\n" + json.dumps(payload, indent=2))
79
+
80
+ # Exit non-zero on any CRITICAL or HIGH issue so CI flows can gate on it.
81
+ if not result.is_clean:
82
+ raise SystemExit(3)
83
+
84
+
85
+ @main.command()
86
+ def examples() -> None:
87
+ """Show usage examples."""
88
+ examples_text = """
89
+ kg-validator examples (v3):
90
+
91
+ 1. Validate a v3 KG file:
92
+ kg-validator validate --input kg-energy-v3.json
93
+
94
+ 2. Validate and save the issue report:
95
+ kg-validator validate --input kg-energy-v3.json --output report.json
96
+
97
+ 3. Compile-extract-validate flow:
98
+ kg-extractor extract --file regulation.txt --output kg-extracted.json --jurisdiction us.federal
99
+ kg-validator validate --input kg-extracted.json --output validation-report.json
100
+ """
101
+ click.echo(examples_text)
102
+
103
+
104
+ @main.command()
105
+ def version() -> None:
106
+ """Show version."""
107
+ click.echo(f"kg-validator {__version__}")
108
+
109
+
110
+ if __name__ == "__main__":
111
+ main()
@@ -0,0 +1,117 @@
1
+ """Loader for v3 KG JSON files.
2
+
3
+ The on-disk v3 KG format is:
4
+
5
+ {
6
+ "metadata": {"name": "...", "schema_version": "3.0", ...},
7
+ "nodes": {
8
+ "subjects": [...], "rules": [...], "obligations": [...],
9
+ "definitions": [...], "exemptions": [...], "precedents": [...],
10
+ "jurisdictions": [...], "citations": [...]
11
+ },
12
+ "edges": [
13
+ {"type": "applies_to", "from": "...", "to": "..."},
14
+ ...
15
+ ]
16
+ }
17
+
18
+ Each node array contains objects matching the corresponding Pydantic
19
+ model in :mod:`.models`. Edges use ``from`` / ``to`` keys; the
20
+ :class:`Edge` model declares aliases so the keys can be used directly.
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import json
26
+ from pathlib import Path
27
+ from typing import Any, Dict, List, Optional
28
+
29
+ from pydantic import BaseModel, ConfigDict, Field
30
+
31
+ from .models import (
32
+ Citation,
33
+ Definition,
34
+ Edge,
35
+ Exemption,
36
+ Jurisdiction,
37
+ Obligation,
38
+ Precedent,
39
+ Rule,
40
+ Subject,
41
+ )
42
+
43
+
44
+ class KGV3Metadata(BaseModel):
45
+ """KG-level metadata (informative; not used in validation passes)."""
46
+
47
+ name: str
48
+ schema_version: str = "3.0"
49
+ sector: Optional[str] = None
50
+ description: Optional[str] = None
51
+ compiled_at: Optional[str] = None
52
+
53
+ model_config = ConfigDict(extra="allow")
54
+
55
+
56
+ class KGV3Nodes(BaseModel):
57
+ """Container for all node arrays. All keys default to empty lists."""
58
+
59
+ subjects: List[Subject] = Field(default_factory=list)
60
+ rules: List[Rule] = Field(default_factory=list)
61
+ obligations: List[Obligation] = Field(default_factory=list)
62
+ definitions: List[Definition] = Field(default_factory=list)
63
+ exemptions: List[Exemption] = Field(default_factory=list)
64
+ precedents: List[Precedent] = Field(default_factory=list)
65
+ jurisdictions: List[Jurisdiction] = Field(default_factory=list)
66
+ citations: List[Citation] = Field(default_factory=list)
67
+
68
+ model_config = ConfigDict(extra="forbid")
69
+
70
+
71
+ class KnowledgeGraphV3(BaseModel):
72
+ """Top-level v3 KG container."""
73
+
74
+ metadata: KGV3Metadata
75
+ nodes: KGV3Nodes = Field(default_factory=KGV3Nodes)
76
+ edges: List[Edge] = Field(default_factory=list)
77
+
78
+ model_config = ConfigDict(extra="forbid")
79
+
80
+ def all_node_ids(self) -> Dict[str, str]:
81
+ """Map every node id to its type name. Useful for edge validation."""
82
+ out: Dict[str, str] = {}
83
+ for n in self.nodes.subjects:
84
+ out[n.id] = "Subject"
85
+ for n in self.nodes.rules:
86
+ out[n.id] = "Rule"
87
+ for n in self.nodes.obligations:
88
+ out[n.id] = "Obligation"
89
+ for n in self.nodes.definitions:
90
+ out[n.id] = "Definition"
91
+ for n in self.nodes.exemptions:
92
+ out[n.id] = "Exemption"
93
+ for n in self.nodes.precedents:
94
+ out[n.id] = "Precedent"
95
+ for n in self.nodes.jurisdictions:
96
+ out[n.id] = "Jurisdiction"
97
+ for n in self.nodes.citations:
98
+ out[n.id] = "Citation"
99
+ return out
100
+
101
+
102
+ def load_v3_kg(path: str) -> KnowledgeGraphV3:
103
+ """Load and validate the Pydantic shape of a v3 KG JSON file.
104
+
105
+ Raises :class:`pydantic.ValidationError` if the file does not
106
+ conform to the schema. Returns the parsed :class:`KnowledgeGraphV3`
107
+ on success.
108
+
109
+ Schema-level validity is necessary but not sufficient. Run
110
+ :class:`kg_validator.V3Validator` against the loaded KG to
111
+ surface the cross-cutting issues from spec §3.6.
112
+ """
113
+ text = Path(path).read_text(encoding="utf-8")
114
+ raw: Any = json.loads(text)
115
+ if not isinstance(raw, dict):
116
+ raise ValueError(f"Top-level JSON in {path} must be an object, got {type(raw).__name__}")
117
+ return KnowledgeGraphV3.model_validate(raw)