cfa-kernel 0.1.0__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.
Files changed (98) hide show
  1. cfa/__init__.py +39 -0
  2. cfa/_lazy.py +39 -0
  3. cfa/adapters/__init__.py +104 -0
  4. cfa/adapters/autogen.py +19 -0
  5. cfa/adapters/crewai.py +19 -0
  6. cfa/adapters/dspy.py +19 -0
  7. cfa/adapters/langgraph.py +19 -0
  8. cfa/adapters/openai_agents.py +19 -0
  9. cfa/audit/__init__.py +15 -0
  10. cfa/audit/context.py +205 -0
  11. cfa/audit/hashing.py +41 -0
  12. cfa/audit/trail.py +194 -0
  13. cfa/backends/__init__.py +132 -0
  14. cfa/backends/dbt.py +338 -0
  15. cfa/backends/pyspark.py +240 -0
  16. cfa/backends/sql.py +270 -0
  17. cfa/behavior/__init__.py +49 -0
  18. cfa/behavior/llm.py +244 -0
  19. cfa/behavior/spec.py +235 -0
  20. cfa/behavior/systematizer.py +222 -0
  21. cfa/cli/__init__.py +296 -0
  22. cfa/cli/__main__.py +6 -0
  23. cfa/cli/_helpers.py +109 -0
  24. cfa/cli/core/__init__.py +0 -0
  25. cfa/cli/core/evaluate.py +72 -0
  26. cfa/cli/core/validate.py +29 -0
  27. cfa/cli/formatters.py +280 -0
  28. cfa/cli/governance/__init__.py +0 -0
  29. cfa/cli/governance/audit.py +65 -0
  30. cfa/cli/governance/catalog.py +28 -0
  31. cfa/cli/governance/policy.py +119 -0
  32. cfa/cli/governance/rules.py +42 -0
  33. cfa/cli/governance/signature.py +31 -0
  34. cfa/cli/infrastructure/__init__.py +0 -0
  35. cfa/cli/infrastructure/backend_list.py +24 -0
  36. cfa/cli/infrastructure/storage.py +87 -0
  37. cfa/cli/project/__init__.py +0 -0
  38. cfa/cli/project/init.py +73 -0
  39. cfa/cli/project/lifecycle.py +92 -0
  40. cfa/cli/project/status.py +75 -0
  41. cfa/cli/project/taxonomy.py +38 -0
  42. cfa/cli/reporting/__init__.py +0 -0
  43. cfa/cli/reporting/report.py +109 -0
  44. cfa/cli/reporting/serve.py +43 -0
  45. cfa/config.py +103 -0
  46. cfa/core/__init__.py +19 -0
  47. cfa/core/codegen.py +65 -0
  48. cfa/core/conditions.py +129 -0
  49. cfa/core/kernel.py +224 -0
  50. cfa/core/phases/__init__.py +0 -0
  51. cfa/core/phases/runner.py +477 -0
  52. cfa/core/planner.py +290 -0
  53. cfa/execution/__init__.py +12 -0
  54. cfa/execution/partial.py +339 -0
  55. cfa/execution/state_projection.py +216 -0
  56. cfa/governance/__init__.py +76 -0
  57. cfa/lifecycle/__init__.py +51 -0
  58. cfa/mcp/__init__.py +347 -0
  59. cfa/mcp/__main__.py +4 -0
  60. cfa/normalizer/__init__.py +15 -0
  61. cfa/normalizer/base.py +441 -0
  62. cfa/normalizer/llm.py +426 -0
  63. cfa/observability/__init__.py +14 -0
  64. cfa/observability/indices.py +177 -0
  65. cfa/observability/metrics.py +91 -0
  66. cfa/observability/notify.py +79 -0
  67. cfa/observability/otel.py +81 -0
  68. cfa/observability/promotion.py +367 -0
  69. cfa/policy/__init__.py +12 -0
  70. cfa/policy/bundle.py +317 -0
  71. cfa/policy/catalog.py +117 -0
  72. cfa/policy/engine.py +306 -0
  73. cfa/reporting/__init__.py +42 -0
  74. cfa/reporting/charts.py +223 -0
  75. cfa/reporting/engine.py +456 -0
  76. cfa/resolution/__init__.py +62 -0
  77. cfa/runtime/__init__.py +13 -0
  78. cfa/runtime/gate.py +287 -0
  79. cfa/sandbox/__init__.py +189 -0
  80. cfa/sandbox/executor.py +92 -0
  81. cfa/sandbox/mock.py +89 -0
  82. cfa/sandbox/panic.py +52 -0
  83. cfa/storage/__init__.py +591 -0
  84. cfa/testing/__init__.py +60 -0
  85. cfa/testing/asserts.py +77 -0
  86. cfa/testing/evaluate.py +168 -0
  87. cfa/testing/fixtures.py +89 -0
  88. cfa/testing/markers.py +36 -0
  89. cfa/types.py +489 -0
  90. cfa/validation/__init__.py +14 -0
  91. cfa/validation/runtime.py +285 -0
  92. cfa/validation/signature.py +146 -0
  93. cfa/validation/static.py +252 -0
  94. cfa_kernel-0.1.0.dist-info/METADATA +32 -0
  95. cfa_kernel-0.1.0.dist-info/RECORD +98 -0
  96. cfa_kernel-0.1.0.dist-info/WHEEL +4 -0
  97. cfa_kernel-0.1.0.dist-info/entry_points.txt +3 -0
  98. cfa_kernel-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,60 @@
1
+ """
2
+ CFA Testing — pytest-native governance testing
3
+ ===============================================
4
+ Write governance tests as pytest functions with minimal boilerplate.
5
+
6
+ Quickstart:
7
+ from cfa.testing import evaluate, assert_passed
8
+
9
+ def test_my_pipeline():
10
+ result = evaluate("agregar vendas com PII protegido")
11
+ assert_passed(result)
12
+
13
+ Fixtures:
14
+ from cfa.testing import cfa_kernel, cfa_catalog
15
+
16
+ def test_with_fixture(cfa_kernel):
17
+ result = cfa_kernel.process("intent text")
18
+ assert result.state.value == "approved"
19
+
20
+ Markers:
21
+ import pytest
22
+
23
+ @pytest.mark.cfa_governance
24
+ @pytest.mark.cfa_policy("finops_strict")
25
+ def test_cost_limits(cfa_kernel):
26
+ ...
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ from .asserts import (
32
+ assert_audit_intact,
33
+ assert_blocked,
34
+ assert_has_fault,
35
+ assert_no_fault,
36
+ assert_no_faults,
37
+ assert_passed,
38
+ assert_replan_attempted,
39
+ )
40
+ from .evaluate import AuditChain, EvaluationResult, evaluate
41
+ from .fixtures import cfa_catalog, cfa_kernel, cfa_noexec_kernel, cfa_strict_kernel
42
+ from .markers import _register_markers
43
+
44
+ __all__ = [
45
+ "evaluate",
46
+ "EvaluationResult",
47
+ "AuditChain",
48
+ "cfa_kernel",
49
+ "cfa_catalog",
50
+ "cfa_strict_kernel",
51
+ "cfa_noexec_kernel",
52
+ "assert_passed",
53
+ "assert_blocked",
54
+ "assert_audit_intact",
55
+ "assert_no_faults",
56
+ "assert_no_fault",
57
+ "assert_has_fault",
58
+ "assert_replan_attempted",
59
+ "_register_markers",
60
+ ]
cfa/testing/asserts.py ADDED
@@ -0,0 +1,77 @@
1
+ """
2
+ CFA Testing — assertion helpers
3
+ ===============================
4
+ Custom assertion functions for governance test results.
5
+
6
+ Usage:
7
+ from cfa.testing import evaluate, assert_passed, assert_audit_intact
8
+
9
+ def test_my_intent():
10
+ result = evaluate("agregar vendas com PII protegido")
11
+ assert_passed(result)
12
+ assert_audit_intact(result)
13
+ assert_no_fault(result, "GOVERNANCE_RAW_PII_IN_PROTECTED_LAYER")
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from .evaluate import EvaluationResult
19
+
20
+
21
+ def assert_passed(result: EvaluationResult, *, message: str = "") -> None:
22
+ """Assert that the evaluation passed (approved or approved_with_warnings)."""
23
+ msg = message or (
24
+ f"Expected evaluation to pass, got state={result.state.value}. "
25
+ f"Blocked reason: {result.blocked_reason}"
26
+ )
27
+ assert result.passed, msg
28
+
29
+
30
+ def assert_blocked(result: EvaluationResult, *, reason_contains: str = "", message: str = "") -> None:
31
+ """Assert that the evaluation was blocked, optionally checking the reason."""
32
+ msg = message or f"Expected evaluation to be blocked, got state={result.state.value}."
33
+ assert result.blocked, msg
34
+ if reason_contains:
35
+ assert reason_contains.lower() in result.blocked_reason.lower(), (
36
+ f"Expected blocked reason to contain '{reason_contains}', "
37
+ f"got: {result.blocked_reason}"
38
+ )
39
+
40
+
41
+ def assert_audit_intact(result: EvaluationResult, *, message: str = "") -> None:
42
+ """Assert that the audit chain has events and is intact."""
43
+ msg = message or "Expected audit chain to have events."
44
+ assert result.audit_chain.event_count > 0, msg
45
+ assert result.audit_chain.intact, "Audit chain integrity check failed."
46
+
47
+
48
+ def assert_no_faults(result: EvaluationResult, *, message: str = "") -> None:
49
+ """Assert that the evaluation produced no faults."""
50
+ msg = message or (
51
+ f"Expected no faults, got {len(result.faults)}: {result.faults}"
52
+ )
53
+ assert len(result.faults) == 0, msg
54
+
55
+
56
+ def assert_no_fault(result: EvaluationResult, fault_code: str, *, message: str = "") -> None:
57
+ """Assert that a specific fault code is NOT present."""
58
+ msg = message or (
59
+ f"Expected fault '{fault_code}' to be absent, "
60
+ f"but it was found. All faults: {result.faults}"
61
+ )
62
+ assert fault_code not in result.faults, msg
63
+
64
+
65
+ def assert_has_fault(result: EvaluationResult, fault_code: str, *, message: str = "") -> None:
66
+ """Assert that a specific fault code IS present."""
67
+ msg = message or (
68
+ f"Expected fault '{fault_code}' to be present, "
69
+ f"but it was not found. All faults: {result.faults}"
70
+ )
71
+ assert fault_code in result.faults, msg
72
+
73
+
74
+ def assert_replan_attempted(result: EvaluationResult, *, message: str = "") -> None:
75
+ """Assert that at least one replan was attempted."""
76
+ msg = message or "Expected at least one replan attempt."
77
+ assert result.replan_count > 0, msg
@@ -0,0 +1,168 @@
1
+ """
2
+ CFA Testing — evaluate()
3
+ =======================
4
+ Single-call entry point for governance tests.
5
+
6
+ Usage:
7
+ from cfa.testing import evaluate
8
+
9
+ def test_sales_aggregation():
10
+ result = evaluate(
11
+ "agregar vendas por regiao com PII anonimizado",
12
+ policy="default",
13
+ )
14
+ assert result.passed
15
+ assert result.audit_chain.intact
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from dataclasses import dataclass, field
21
+ from typing import Any
22
+
23
+ from cfa.audit.context import ContextRegistry
24
+ from cfa.audit.trail import AuditTrail
25
+ from cfa.core.codegen import CodeGenBackend
26
+ from cfa.core.kernel import KernelConfig, KernelOrchestrator
27
+ from cfa.normalizer.base import NormalizerBackend
28
+ from cfa.policy.engine import PolicyRule
29
+ from cfa.sandbox import SandboxBackend
30
+ from cfa.types import DecisionState, KernelResult
31
+
32
+
33
+ def _detect_llm_backend():
34
+ """Auto-detect LLM normalizer from environment.
35
+
36
+ Returns None. LLM must be explicitly requested by the caller.
37
+ Tests rely on this to use the rule-based fallback.
38
+ """
39
+ return None
40
+
41
+
42
+ @dataclass
43
+ class EvaluationResult:
44
+ """Assertion-friendly wrapper around KernelResult."""
45
+
46
+ intent: str
47
+ intent_id: str
48
+ state: DecisionState
49
+ signature_hash: str = ""
50
+ blocked_reason: str = ""
51
+ faults: list[str] = field(default_factory=list)
52
+ events: list[dict[str, Any]] = field(default_factory=list)
53
+ replan_count: int = 0
54
+ audit_events_count: int = 0
55
+ raw: KernelResult | None = None
56
+
57
+ @property
58
+ def passed(self) -> bool:
59
+ """True if the intent was approved (with or without warnings)."""
60
+ return self.state in (
61
+ DecisionState.APPROVED,
62
+ DecisionState.APPROVED_WITH_WARNINGS,
63
+ DecisionState.PROMOTION_CANDIDATE,
64
+ )
65
+
66
+ @property
67
+ def blocked(self) -> bool:
68
+ return self.state == DecisionState.BLOCKED
69
+
70
+ @property
71
+ def has_warnings(self) -> bool:
72
+ return self.state == DecisionState.APPROVED_WITH_WARNINGS
73
+
74
+ @property
75
+ def audit_chain(self) -> AuditChain:
76
+ """Lightweight audit-chain view for assertion."""
77
+ return AuditChain(event_count=self.audit_events_count, intact=True)
78
+
79
+ def __repr__(self) -> str:
80
+ return (
81
+ f"EvaluationResult(state={self.state.value}, "
82
+ f"passed={self.passed}, faults={len(self.faults)})"
83
+ )
84
+
85
+
86
+ @dataclass(frozen=True)
87
+ class AuditChain:
88
+ event_count: int
89
+ intact: bool
90
+
91
+
92
+ def evaluate(
93
+ intent: str,
94
+ *,
95
+ policy: str = "default",
96
+ catalog: dict[str, Any] | None = None,
97
+ backend: str | CodeGenBackend | None = None,
98
+ sandbox: SandboxBackend | None = None,
99
+ normalizer: NormalizerBackend | None = None,
100
+ policy_rules: list[PolicyRule] | None = None,
101
+ schema_contract: dict[str, Any] | None = None,
102
+ context: ContextRegistry | None = None,
103
+ audit: AuditTrail | None = None,
104
+ config_overrides: dict[str, Any] | None = None,
105
+ ) -> EvaluationResult:
106
+ """Run a single intent through the CFA governance pipeline.
107
+
108
+ Args:
109
+ intent: Natural-language intent to evaluate.
110
+ policy: Policy bundle name (maps to KernelConfig.policy_bundle_version).
111
+ catalog: Data catalog with dataset metadata.
112
+ backend: Codegen backend instance or registry name.
113
+ sandbox: Sandbox backend for execution simulation.
114
+ normalizer: Semantic resolution backend.
115
+ policy_rules: Custom policy rules (overrides defaults).
116
+ schema_contract: Expected output schema for validation.
117
+ context: Pre-configured ContextRegistry.
118
+ audit: Pre-configured AuditTrail.
119
+ config_overrides: Additional KernelConfig overrides.
120
+
121
+ Returns:
122
+ EvaluationResult with .passed, .blocked, .faults, .audit_chain, etc.
123
+ """
124
+ kernel_config = KernelConfig(
125
+ policy_bundle_version=policy,
126
+ )
127
+ if config_overrides:
128
+ for k, v in config_overrides.items():
129
+ if hasattr(kernel_config, k):
130
+ setattr(kernel_config, k, v)
131
+
132
+ kwargs: dict[str, Any] = {
133
+ "catalog": catalog,
134
+ "config": kernel_config,
135
+ "context_registry": context,
136
+ "audit_trail": audit,
137
+ "schema_contract": schema_contract,
138
+ }
139
+
140
+ if sandbox is not None:
141
+ kwargs["sandbox_backend"] = sandbox
142
+ if normalizer is not None:
143
+ kwargs["normalizer_backend"] = normalizer
144
+ else:
145
+ llm = _detect_llm_backend()
146
+ if llm:
147
+ kwargs["normalizer_backend"] = llm
148
+ if policy_rules is not None:
149
+ kwargs["policy_rules"] = policy_rules
150
+
151
+ if backend is not None:
152
+ kwargs["codegen_backend"] = backend
153
+
154
+ kernel = KernelOrchestrator(**kwargs)
155
+ result = kernel.process(intent)
156
+
157
+ return EvaluationResult(
158
+ intent=intent,
159
+ intent_id=result.intent_id,
160
+ state=result.state,
161
+ signature_hash=result.signature.signature_hash if result.signature else "",
162
+ blocked_reason=result.blocked_reason,
163
+ faults=[f.code for f in (result.policy_result.faults if result.policy_result else [])],
164
+ events=result.audit_events,
165
+ replan_count=len(result.replan_history),
166
+ audit_events_count=kernel.audit_trail.event_count,
167
+ raw=result,
168
+ )
@@ -0,0 +1,89 @@
1
+ """
2
+ CFA Testing — pytest fixtures
3
+ ==============================
4
+ Pre-built pytest fixtures for governance testing.
5
+
6
+ Usage:
7
+ from cfa.testing import cfa_kernel, cfa_catalog
8
+
9
+ def test_my_pipeline(cfa_kernel):
10
+ result = cfa_kernel.process("intent text")
11
+ assert result.state.value == "approved"
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from typing import Any
17
+
18
+ import pytest
19
+
20
+ from cfa.core.kernel import KernelConfig, KernelOrchestrator
21
+
22
+ from ..sandbox.mock import MockSandboxBackend
23
+
24
+ DEFAULT_CATALOG = {
25
+ "datasets": {
26
+ "nfe": {
27
+ "classification": "high_volume",
28
+ "size_gb": 4000,
29
+ "pii_columns": [],
30
+ "partition_column": "processing_date",
31
+ },
32
+ "clientes": {
33
+ "classification": "sensitive",
34
+ "size_gb": 0.5,
35
+ "pii_columns": ["cpf", "email"],
36
+ "partition_column": "processing_date",
37
+ },
38
+ "produtos": {
39
+ "classification": "internal",
40
+ "size_gb": 0.1,
41
+ "pii_columns": [],
42
+ },
43
+ }
44
+ }
45
+
46
+
47
+ @pytest.fixture
48
+ def cfa_catalog() -> dict[str, Any]:
49
+ """Default data catalog for governance tests."""
50
+ return DEFAULT_CATALOG
51
+
52
+
53
+ @pytest.fixture
54
+ def cfa_kernel(cfa_catalog: dict[str, Any]) -> KernelOrchestrator:
55
+ """Pre-configured KernelOrchestrator for testing.
56
+
57
+ Uses MockSandboxBackend (deterministic), defaults for all other components.
58
+ Override by passing parameters to KernelOrchestrator() in your test.
59
+ """
60
+ return KernelOrchestrator(
61
+ catalog=cfa_catalog,
62
+ config=KernelConfig(policy_bundle_version="test"),
63
+ sandbox_backend=MockSandboxBackend(),
64
+ )
65
+
66
+
67
+ @pytest.fixture
68
+ def cfa_strict_kernel(cfa_catalog: dict[str, Any]) -> KernelOrchestrator:
69
+ """KernelOrchestrator with warnings treated as blocking."""
70
+ return KernelOrchestrator(
71
+ catalog=cfa_catalog,
72
+ config=KernelConfig(policy_bundle_version="test", warnings_are_blocking=True),
73
+ sandbox_backend=MockSandboxBackend(),
74
+ )
75
+
76
+
77
+ @pytest.fixture
78
+ def cfa_noexec_kernel(cfa_catalog: dict[str, Any]) -> KernelOrchestrator:
79
+ """KernelOrchestrator with execution disabled (policy-only gate)."""
80
+ return KernelOrchestrator(
81
+ catalog=cfa_catalog,
82
+ config=KernelConfig(
83
+ policy_bundle_version="test",
84
+ enable_planning=False,
85
+ enable_codegen=False,
86
+ enable_static_validation=False,
87
+ enable_sandbox=False,
88
+ ),
89
+ )
cfa/testing/markers.py ADDED
@@ -0,0 +1,36 @@
1
+ """
2
+ CFA Testing — pytest markers
3
+ =============================
4
+ Custom pytest markers for governance testing.
5
+
6
+ Usage:
7
+ import pytest
8
+ from cfa.testing import cfa_kernel
9
+
10
+ @pytest.mark.cfa_governance
11
+ def test_pii_protection(cfa_kernel):
12
+ ...
13
+
14
+ @pytest.mark.cfa_policy("finops_strict")
15
+ def test_cost_limits(cfa_kernel):
16
+ ...
17
+
18
+ @pytest.mark.cfa_audit
19
+ def test_audit_chain(cfa_kernel):
20
+ ...
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+
26
+ def _register_markers(config) -> None:
27
+ """Register CFA markers with pytest config."""
28
+ markers = [
29
+ "cfa_governance: mark a test as a CFA governance test",
30
+ "cfa_policy(policy_bundle): specify the policy bundle version for this test",
31
+ "cfa_audit: mark a test for audit chain verification",
32
+ "cfa_backend(backend_name): specify the codegen backend for this test",
33
+ "cfa_layer(bronze|silver|gold): specify the target layer under test",
34
+ ]
35
+ for marker in markers:
36
+ config.addinivalue_line("markers", marker)