gateframe 0.1.0__tar.gz → 0.2.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.
Files changed (41) hide show
  1. gateframe-0.2.0/PKG-INFO +289 -0
  2. gateframe-0.2.0/README.md +252 -0
  3. gateframe-0.2.0/gateframe/__init__.py +24 -0
  4. gateframe-0.2.0/gateframe/audit/exporters.py +112 -0
  5. gateframe-0.2.0/gateframe/audit/log.py +106 -0
  6. gateframe-0.2.0/gateframe/cli/inspect.py +73 -0
  7. gateframe-0.2.0/gateframe/cli/main.py +33 -0
  8. gateframe-0.2.0/gateframe/cli/replay.py +77 -0
  9. gateframe-0.2.0/gateframe/core/context.py +130 -0
  10. {gateframe-0.1.0 → gateframe-0.2.0}/gateframe/core/contract.py +17 -2
  11. gateframe-0.2.0/gateframe/core/escalation.py +102 -0
  12. gateframe-0.2.0/gateframe/integrations/anthropic.py +78 -0
  13. gateframe-0.2.0/gateframe/integrations/langchain.py +96 -0
  14. gateframe-0.2.0/gateframe/integrations/litellm.py +82 -0
  15. gateframe-0.2.0/gateframe/integrations/openai.py +82 -0
  16. gateframe-0.2.0/gateframe/rules/__init__.py +1 -0
  17. gateframe-0.2.0/gateframe/rules/boundary.py +58 -0
  18. gateframe-0.2.0/gateframe/rules/confidence.py +112 -0
  19. gateframe-0.2.0/gateframe/rules/semantic.py +65 -0
  20. gateframe-0.2.0/gateframe.egg-info/PKG-INFO +289 -0
  21. gateframe-0.2.0/gateframe.egg-info/SOURCES.txt +33 -0
  22. gateframe-0.2.0/gateframe.egg-info/entry_points.txt +2 -0
  23. gateframe-0.2.0/gateframe.egg-info/requires.txt +24 -0
  24. {gateframe-0.1.0 → gateframe-0.2.0}/pyproject.toml +13 -1
  25. gateframe-0.1.0/PKG-INFO +0 -160
  26. gateframe-0.1.0/README.md +0 -135
  27. gateframe-0.1.0/gateframe/audit/log.py +0 -69
  28. gateframe-0.1.0/gateframe.egg-info/PKG-INFO +0 -160
  29. gateframe-0.1.0/gateframe.egg-info/SOURCES.txt +0 -17
  30. gateframe-0.1.0/gateframe.egg-info/requires.txt +0 -7
  31. {gateframe-0.1.0 → gateframe-0.2.0}/LICENSE +0 -0
  32. {gateframe-0.1.0/gateframe → gateframe-0.2.0/gateframe/audit}/__init__.py +0 -0
  33. {gateframe-0.1.0/gateframe/audit → gateframe-0.2.0/gateframe/cli}/__init__.py +0 -0
  34. {gateframe-0.1.0 → gateframe-0.2.0}/gateframe/core/__init__.py +0 -0
  35. {gateframe-0.1.0 → gateframe-0.2.0}/gateframe/core/failure.py +0 -0
  36. {gateframe-0.1.0 → gateframe-0.2.0}/gateframe/core/rule.py +0 -0
  37. {gateframe-0.1.0/gateframe/rules → gateframe-0.2.0/gateframe/integrations}/__init__.py +0 -0
  38. {gateframe-0.1.0 → gateframe-0.2.0}/gateframe/rules/structural.py +0 -0
  39. {gateframe-0.1.0 → gateframe-0.2.0}/gateframe.egg-info/dependency_links.txt +0 -0
  40. {gateframe-0.1.0 → gateframe-0.2.0}/gateframe.egg-info/top_level.txt +0 -0
  41. {gateframe-0.1.0 → gateframe-0.2.0}/setup.cfg +0 -0
@@ -0,0 +1,289 @@
1
+ Metadata-Version: 2.4
2
+ Name: gateframe
3
+ Version: 0.2.0
4
+ Summary: Behavioral validation for LLM outputs in production workflows.
5
+ License-Expression: MIT
6
+ Keywords: llm,validation,behavioral,workflow,ai
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Typing :: Typed
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: pydantic<3.0,>=2.0
19
+ Requires-Dist: structlog>=23.0
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest>=7.0; extra == "dev"
22
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
23
+ Requires-Dist: ruff>=0.4.0; extra == "dev"
24
+ Provides-Extra: anthropic
25
+ Requires-Dist: anthropic>=0.30.0; extra == "anthropic"
26
+ Provides-Extra: openai
27
+ Requires-Dist: openai>=1.0.0; extra == "openai"
28
+ Provides-Extra: litellm
29
+ Requires-Dist: litellm>=1.0.0; extra == "litellm"
30
+ Provides-Extra: otlp
31
+ Requires-Dist: opentelemetry-api>=1.20.0; extra == "otlp"
32
+ Requires-Dist: opentelemetry-sdk>=1.20.0; extra == "otlp"
33
+ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.20.0; extra == "otlp"
34
+ Provides-Extra: langchain
35
+ Requires-Dist: langchain>=0.1.0; extra == "langchain"
36
+ Dynamic: license-file
37
+
38
+ # Gateframe
39
+
40
+ Behavioral validation for LLM outputs in production workflows.
41
+
42
+ Schema validation, "does this JSON have the right keys?", is a solved problem. Instructor, Pydantic AI, and similar tools handle it well. gateframe solves a different problem: **does this output behave correctly given the context it was generated in?** Does it stay within the decision boundaries this workflow requires? When it fails, does it fail in a way your system can recover from, or does it fail silently?
43
+
44
+ ```python
45
+ from pydantic import BaseModel
46
+ from gateframe import (
47
+ ValidationContract,
48
+ StructuralRule,
49
+ BoundaryRule,
50
+ ConfidenceRule,
51
+ AllowedValues,
52
+ FailureMode,
53
+ )
54
+
55
+ class TriageDecision(BaseModel):
56
+ action: str
57
+ priority: str
58
+ confidence: float
59
+ rationale: str
60
+
61
+ contract = ValidationContract(
62
+ name="triage_decision",
63
+ rules=[
64
+ StructuralRule(schema=TriageDecision),
65
+ BoundaryRule(
66
+ check=AllowedValues("action", {"treat", "observe", "refer", "discharge"}),
67
+ name="action_boundary",
68
+ failure_message="Action must be one of: treat, observe, refer, discharge.",
69
+ ),
70
+ ConfidenceRule(field="confidence", minimum=0.7),
71
+ ],
72
+ )
73
+
74
+ result = contract.validate({
75
+ "action": "prescribe", # not in allowed set -> HARD_FAIL
76
+ "priority": "high",
77
+ "confidence": 0.52, # below 0.7 -> SOFT_FAIL
78
+ "rationale": "...",
79
+ })
80
+
81
+ print(result.passed) # False
82
+ for failure in result.failures:
83
+ print(f"[{failure.failure_mode.value}] {failure.rule_name}: {failure.message}")
84
+ # [hard_fail] action_boundary: Action must be one of: treat, observe, refer, discharge.
85
+ # [soft_fail] confidence_check: Confidence 0.52 is below minimum threshold 0.7.
86
+ ```
87
+
88
+ ---
89
+
90
+ ## The problem
91
+
92
+ Most LLM pipelines validate outputs the same way: parse the JSON, check the schema, move on. That catches structural errors. It misses the errors that actually cause production incidents:
93
+
94
+ - A model recommends an action that is structurally valid but outside its authorized scope
95
+ - Confidence is low but the workflow proceeds as if it weren't
96
+ - A soft failure in step 2 silently degrades the reliability of everything downstream
97
+ - A validation failure gives you `False`, and no context for debugging
98
+
99
+ gateframe makes these failures explicit, structured, and recoverable.
100
+
101
+ ---
102
+
103
+ ## Failure modes
104
+
105
+ gateframe distinguishes four failure types instead of binary pass/fail.
106
+
107
+ **`HARD_FAIL`**, Stop. The output violates a hard constraint that cannot be auto-recovered.
108
+
109
+ ```python
110
+ # Model chose an action outside its authorized scope
111
+ BoundaryRule(
112
+ check=AllowedValues("action", {"treat", "observe", "refer"}),
113
+ failure_mode=FailureMode.HARD_FAIL, # default for BoundaryRule
114
+ )
115
+ ```
116
+
117
+ **`SOFT_FAIL`**, Flag and continue with degraded confidence. Something is off but not critical enough to halt.
118
+
119
+ ```python
120
+ # Model confidence is low, continue but track the degradation
121
+ ConfidenceRule(
122
+ field="confidence",
123
+ minimum=0.7,
124
+ failure_mode=FailureMode.SOFT_FAIL, # default for ConfidenceRule
125
+ )
126
+ ```
127
+
128
+ **`RETRY`**, Re-prompt with the failure context. The output is likely fixable by trying again.
129
+
130
+ ```python
131
+ # Malformed output that might parse correctly on a second attempt
132
+ StructuralRule(schema=MyOutput, failure_mode=FailureMode.RETRY)
133
+ ```
134
+
135
+ **`SILENT_FAIL`**, The most dangerous kind. The output looks valid but violates a semantic or boundary rule. gateframe makes these visible instead of letting them pass through undetected.
136
+
137
+ ```python
138
+ SemanticRule(
139
+ check=lambda output, **ctx: output["severity"] != "low" or output["escalated"] is False,
140
+ failure_mode=FailureMode.SILENT_FAIL,
141
+ failure_message="Low-severity cases should not be auto-escalated.",
142
+ )
143
+ ```
144
+
145
+ ---
146
+
147
+ ## Multi-step workflow validation
148
+
149
+ Validation state carries forward across steps. A soft failure in step 2 degrades the confidence score that step 4 sees.
150
+
151
+ ```python
152
+ from gateframe import WorkflowContext, ValidationContract, EscalationRouter
153
+ from gateframe.audit.log import AuditLog
154
+
155
+ ctx = WorkflowContext(workflow_id="incident_response_001", escalation_threshold=0.5)
156
+ router = EscalationRouter()
157
+ audit = AuditLog()
158
+
159
+ # Step 1
160
+ result1 = contract_step1.validate(output1)
161
+ ctx.update(result1)
162
+ audit.record(result1, workflow_context=ctx)
163
+
164
+ # Step 2, ctx carries forward degraded confidence from step 1
165
+ result2 = contract_step2.validate(output2)
166
+ ctx.update(result2)
167
+ audit.record(result2, workflow_context=ctx)
168
+
169
+ print(ctx.confidence) # degraded from 1.0 by soft failures
170
+ print(ctx.threshold_breached) # True if confidence < escalation_threshold
171
+
172
+ if ctx.threshold_breached:
173
+ escalation = router.route_threshold_breach(ctx)
174
+ print(escalation.route.value) # "human_review", "abort", etc.
175
+ ```
176
+
177
+ ---
178
+
179
+ ## Provider integrations
180
+
181
+ gateframe validates outputs from any provider. Integrations are thin wrappers, gateframe does not import any LLM SDK at the core level.
182
+
183
+ ```python
184
+ # OpenAI
185
+ from gateframe.integrations.openai import OpenAIValidator
186
+ validator = OpenAIValidator(contract, parse_json=True)
187
+ result = validator.validate(openai_completion)
188
+
189
+ # Anthropic
190
+ from gateframe.integrations.anthropic import AnthropicValidator
191
+ validator = AnthropicValidator(contract, parse_json=True)
192
+ result = validator.validate(anthropic_message)
193
+
194
+ # LiteLLM
195
+ from gateframe.integrations.litellm import LiteLLMValidator
196
+ validator = LiteLLMValidator(contract, parse_json=True)
197
+ result = validator.validate(litellm_response)
198
+
199
+ # LangChain
200
+ from gateframe.integrations.langchain import LangChainValidator
201
+ validator = LangChainValidator(contract, parse_json=False)
202
+ result = validator.validate(chain_output)
203
+ ```
204
+
205
+ Install the integration you need:
206
+
207
+ ```bash
208
+ pip install "gateframe[openai]"
209
+ pip install "gateframe[anthropic]"
210
+ pip install "gateframe[litellm]"
211
+ pip install "gateframe[langchain]"
212
+ ```
213
+
214
+ ---
215
+
216
+ ## Audit trail
217
+
218
+ Every validation event is logged with structured context. Use the built-in exporters or implement your own.
219
+
220
+ ```python
221
+ from gateframe.audit.log import AuditLog
222
+ from gateframe.audit.exporters import JsonFileExporter
223
+
224
+ audit = AuditLog(exporters=[JsonFileExporter("audit.jsonl")])
225
+ audit.record(result, workflow_context=ctx)
226
+ audit.flush()
227
+ ```
228
+
229
+ Each entry includes: timestamp, contract name, rules applied, rules failed, failure details, workflow ID, and accumulated confidence score.
230
+
231
+ ---
232
+
233
+ ## When to use gateframe
234
+
235
+ Use it when:
236
+ - You need to validate LLM output behavior beyond schema checks, decision boundaries, scope enforcement, semantic constraints
237
+ - You need structured, recoverable failure records rather than bare exceptions
238
+ - You're running multi-step workflows where soft failures in early steps should affect confidence downstream
239
+ - You need an audit trail for post-incident debugging
240
+
241
+ Don't use it when:
242
+ - You only need schema extraction from LLM outputs, use [Instructor](https://github.com/jxnl/instructor) or [Pydantic AI](https://ai.pydantic.dev/)
243
+ - You need offline model evaluation or benchmarking, use [DeepEval](https://github.com/confident-ai/deepeval) or [RAGAS](https://github.com/explodinggradients/ragas)
244
+ - You need content safety filtering, use a dedicated guardrails tool
245
+
246
+ ---
247
+
248
+ ## Installation
249
+
250
+ ```bash
251
+ pip install gateframe
252
+ ```
253
+
254
+ For development:
255
+
256
+ ```bash
257
+ git clone https://github.com/practicalmind-ai/gateframe.git
258
+ cd gateframe
259
+ pip install -e ".[dev]"
260
+ python -m pytest tests/ -v
261
+ ```
262
+
263
+ ---
264
+
265
+ ## Examples
266
+
267
+ **[triage_workflow](examples/triage_workflow/)**, 3-step medical triage pipeline. Demonstrates StructuralRule, BoundaryRule, ConfidenceRule, and WorkflowContext together. Step 2 has confidence below threshold, shows how SOFT_FAIL degrades the workflow score without halting it.
268
+
269
+ **[rag_output](examples/rag_output/)**, RAG answer validation with two scenarios. Scenario B demonstrates simultaneous soft failures (low confidence + ungrounded answer) and how they accumulate in the workflow context.
270
+
271
+ **[agent_pipeline](examples/agent_pipeline/)**, 4-step agent workflow with escalation. Demonstrates how multiple soft failures across steps push cumulative confidence below the escalation threshold.
272
+
273
+ ---
274
+
275
+ ## CLI
276
+
277
+ ```bash
278
+ # Inspect a contract file, lists all contracts and their rules
279
+ gateframe inspect contracts.py
280
+
281
+ # Replay an audit log
282
+ gateframe replay audit.jsonl
283
+ ```
284
+
285
+ ---
286
+
287
+ ## License
288
+
289
+ MIT, see [LICENSE](LICENSE).
@@ -0,0 +1,252 @@
1
+ # Gateframe
2
+
3
+ Behavioral validation for LLM outputs in production workflows.
4
+
5
+ Schema validation, "does this JSON have the right keys?", is a solved problem. Instructor, Pydantic AI, and similar tools handle it well. gateframe solves a different problem: **does this output behave correctly given the context it was generated in?** Does it stay within the decision boundaries this workflow requires? When it fails, does it fail in a way your system can recover from, or does it fail silently?
6
+
7
+ ```python
8
+ from pydantic import BaseModel
9
+ from gateframe import (
10
+ ValidationContract,
11
+ StructuralRule,
12
+ BoundaryRule,
13
+ ConfidenceRule,
14
+ AllowedValues,
15
+ FailureMode,
16
+ )
17
+
18
+ class TriageDecision(BaseModel):
19
+ action: str
20
+ priority: str
21
+ confidence: float
22
+ rationale: str
23
+
24
+ contract = ValidationContract(
25
+ name="triage_decision",
26
+ rules=[
27
+ StructuralRule(schema=TriageDecision),
28
+ BoundaryRule(
29
+ check=AllowedValues("action", {"treat", "observe", "refer", "discharge"}),
30
+ name="action_boundary",
31
+ failure_message="Action must be one of: treat, observe, refer, discharge.",
32
+ ),
33
+ ConfidenceRule(field="confidence", minimum=0.7),
34
+ ],
35
+ )
36
+
37
+ result = contract.validate({
38
+ "action": "prescribe", # not in allowed set -> HARD_FAIL
39
+ "priority": "high",
40
+ "confidence": 0.52, # below 0.7 -> SOFT_FAIL
41
+ "rationale": "...",
42
+ })
43
+
44
+ print(result.passed) # False
45
+ for failure in result.failures:
46
+ print(f"[{failure.failure_mode.value}] {failure.rule_name}: {failure.message}")
47
+ # [hard_fail] action_boundary: Action must be one of: treat, observe, refer, discharge.
48
+ # [soft_fail] confidence_check: Confidence 0.52 is below minimum threshold 0.7.
49
+ ```
50
+
51
+ ---
52
+
53
+ ## The problem
54
+
55
+ Most LLM pipelines validate outputs the same way: parse the JSON, check the schema, move on. That catches structural errors. It misses the errors that actually cause production incidents:
56
+
57
+ - A model recommends an action that is structurally valid but outside its authorized scope
58
+ - Confidence is low but the workflow proceeds as if it weren't
59
+ - A soft failure in step 2 silently degrades the reliability of everything downstream
60
+ - A validation failure gives you `False`, and no context for debugging
61
+
62
+ gateframe makes these failures explicit, structured, and recoverable.
63
+
64
+ ---
65
+
66
+ ## Failure modes
67
+
68
+ gateframe distinguishes four failure types instead of binary pass/fail.
69
+
70
+ **`HARD_FAIL`**, Stop. The output violates a hard constraint that cannot be auto-recovered.
71
+
72
+ ```python
73
+ # Model chose an action outside its authorized scope
74
+ BoundaryRule(
75
+ check=AllowedValues("action", {"treat", "observe", "refer"}),
76
+ failure_mode=FailureMode.HARD_FAIL, # default for BoundaryRule
77
+ )
78
+ ```
79
+
80
+ **`SOFT_FAIL`**, Flag and continue with degraded confidence. Something is off but not critical enough to halt.
81
+
82
+ ```python
83
+ # Model confidence is low, continue but track the degradation
84
+ ConfidenceRule(
85
+ field="confidence",
86
+ minimum=0.7,
87
+ failure_mode=FailureMode.SOFT_FAIL, # default for ConfidenceRule
88
+ )
89
+ ```
90
+
91
+ **`RETRY`**, Re-prompt with the failure context. The output is likely fixable by trying again.
92
+
93
+ ```python
94
+ # Malformed output that might parse correctly on a second attempt
95
+ StructuralRule(schema=MyOutput, failure_mode=FailureMode.RETRY)
96
+ ```
97
+
98
+ **`SILENT_FAIL`**, The most dangerous kind. The output looks valid but violates a semantic or boundary rule. gateframe makes these visible instead of letting them pass through undetected.
99
+
100
+ ```python
101
+ SemanticRule(
102
+ check=lambda output, **ctx: output["severity"] != "low" or output["escalated"] is False,
103
+ failure_mode=FailureMode.SILENT_FAIL,
104
+ failure_message="Low-severity cases should not be auto-escalated.",
105
+ )
106
+ ```
107
+
108
+ ---
109
+
110
+ ## Multi-step workflow validation
111
+
112
+ Validation state carries forward across steps. A soft failure in step 2 degrades the confidence score that step 4 sees.
113
+
114
+ ```python
115
+ from gateframe import WorkflowContext, ValidationContract, EscalationRouter
116
+ from gateframe.audit.log import AuditLog
117
+
118
+ ctx = WorkflowContext(workflow_id="incident_response_001", escalation_threshold=0.5)
119
+ router = EscalationRouter()
120
+ audit = AuditLog()
121
+
122
+ # Step 1
123
+ result1 = contract_step1.validate(output1)
124
+ ctx.update(result1)
125
+ audit.record(result1, workflow_context=ctx)
126
+
127
+ # Step 2, ctx carries forward degraded confidence from step 1
128
+ result2 = contract_step2.validate(output2)
129
+ ctx.update(result2)
130
+ audit.record(result2, workflow_context=ctx)
131
+
132
+ print(ctx.confidence) # degraded from 1.0 by soft failures
133
+ print(ctx.threshold_breached) # True if confidence < escalation_threshold
134
+
135
+ if ctx.threshold_breached:
136
+ escalation = router.route_threshold_breach(ctx)
137
+ print(escalation.route.value) # "human_review", "abort", etc.
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Provider integrations
143
+
144
+ gateframe validates outputs from any provider. Integrations are thin wrappers, gateframe does not import any LLM SDK at the core level.
145
+
146
+ ```python
147
+ # OpenAI
148
+ from gateframe.integrations.openai import OpenAIValidator
149
+ validator = OpenAIValidator(contract, parse_json=True)
150
+ result = validator.validate(openai_completion)
151
+
152
+ # Anthropic
153
+ from gateframe.integrations.anthropic import AnthropicValidator
154
+ validator = AnthropicValidator(contract, parse_json=True)
155
+ result = validator.validate(anthropic_message)
156
+
157
+ # LiteLLM
158
+ from gateframe.integrations.litellm import LiteLLMValidator
159
+ validator = LiteLLMValidator(contract, parse_json=True)
160
+ result = validator.validate(litellm_response)
161
+
162
+ # LangChain
163
+ from gateframe.integrations.langchain import LangChainValidator
164
+ validator = LangChainValidator(contract, parse_json=False)
165
+ result = validator.validate(chain_output)
166
+ ```
167
+
168
+ Install the integration you need:
169
+
170
+ ```bash
171
+ pip install "gateframe[openai]"
172
+ pip install "gateframe[anthropic]"
173
+ pip install "gateframe[litellm]"
174
+ pip install "gateframe[langchain]"
175
+ ```
176
+
177
+ ---
178
+
179
+ ## Audit trail
180
+
181
+ Every validation event is logged with structured context. Use the built-in exporters or implement your own.
182
+
183
+ ```python
184
+ from gateframe.audit.log import AuditLog
185
+ from gateframe.audit.exporters import JsonFileExporter
186
+
187
+ audit = AuditLog(exporters=[JsonFileExporter("audit.jsonl")])
188
+ audit.record(result, workflow_context=ctx)
189
+ audit.flush()
190
+ ```
191
+
192
+ Each entry includes: timestamp, contract name, rules applied, rules failed, failure details, workflow ID, and accumulated confidence score.
193
+
194
+ ---
195
+
196
+ ## When to use gateframe
197
+
198
+ Use it when:
199
+ - You need to validate LLM output behavior beyond schema checks, decision boundaries, scope enforcement, semantic constraints
200
+ - You need structured, recoverable failure records rather than bare exceptions
201
+ - You're running multi-step workflows where soft failures in early steps should affect confidence downstream
202
+ - You need an audit trail for post-incident debugging
203
+
204
+ Don't use it when:
205
+ - You only need schema extraction from LLM outputs, use [Instructor](https://github.com/jxnl/instructor) or [Pydantic AI](https://ai.pydantic.dev/)
206
+ - You need offline model evaluation or benchmarking, use [DeepEval](https://github.com/confident-ai/deepeval) or [RAGAS](https://github.com/explodinggradients/ragas)
207
+ - You need content safety filtering, use a dedicated guardrails tool
208
+
209
+ ---
210
+
211
+ ## Installation
212
+
213
+ ```bash
214
+ pip install gateframe
215
+ ```
216
+
217
+ For development:
218
+
219
+ ```bash
220
+ git clone https://github.com/practicalmind-ai/gateframe.git
221
+ cd gateframe
222
+ pip install -e ".[dev]"
223
+ python -m pytest tests/ -v
224
+ ```
225
+
226
+ ---
227
+
228
+ ## Examples
229
+
230
+ **[triage_workflow](examples/triage_workflow/)**, 3-step medical triage pipeline. Demonstrates StructuralRule, BoundaryRule, ConfidenceRule, and WorkflowContext together. Step 2 has confidence below threshold, shows how SOFT_FAIL degrades the workflow score without halting it.
231
+
232
+ **[rag_output](examples/rag_output/)**, RAG answer validation with two scenarios. Scenario B demonstrates simultaneous soft failures (low confidence + ungrounded answer) and how they accumulate in the workflow context.
233
+
234
+ **[agent_pipeline](examples/agent_pipeline/)**, 4-step agent workflow with escalation. Demonstrates how multiple soft failures across steps push cumulative confidence below the escalation threshold.
235
+
236
+ ---
237
+
238
+ ## CLI
239
+
240
+ ```bash
241
+ # Inspect a contract file, lists all contracts and their rules
242
+ gateframe inspect contracts.py
243
+
244
+ # Replay an audit log
245
+ gateframe replay audit.jsonl
246
+ ```
247
+
248
+ ---
249
+
250
+ ## License
251
+
252
+ MIT, see [LICENSE](LICENSE).
@@ -0,0 +1,24 @@
1
+ from gateframe.core.context import WorkflowContext
2
+ from gateframe.core.contract import ValidationContract, ValidationResult
3
+ from gateframe.core.escalation import EscalationRoute, EscalationRouter
4
+ from gateframe.core.failure import FailureMode, FailureResult
5
+ from gateframe.rules.boundary import AllowedValues, BoundaryRule
6
+ from gateframe.rules.confidence import ConfidenceRule
7
+ from gateframe.rules.semantic import LlmJudge, SemanticRule
8
+ from gateframe.rules.structural import StructuralRule
9
+
10
+ __all__ = [
11
+ "AllowedValues",
12
+ "BoundaryRule",
13
+ "ConfidenceRule",
14
+ "EscalationRoute",
15
+ "EscalationRouter",
16
+ "FailureMode",
17
+ "FailureResult",
18
+ "LlmJudge",
19
+ "SemanticRule",
20
+ "StructuralRule",
21
+ "ValidationContract",
22
+ "ValidationResult",
23
+ "WorkflowContext",
24
+ ]
@@ -0,0 +1,112 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from abc import ABC, abstractmethod
5
+ from pathlib import Path
6
+ from typing import TYPE_CHECKING, Any
7
+
8
+ import structlog
9
+
10
+ if TYPE_CHECKING:
11
+ from gateframe.audit.log import AuditEntry
12
+
13
+ logger = structlog.get_logger()
14
+
15
+
16
+ class AuditExporter(ABC):
17
+ @abstractmethod
18
+ def export(self, entry: AuditEntry) -> None: ...
19
+
20
+ @abstractmethod
21
+ def flush(self) -> None: ...
22
+
23
+ def shutdown(self) -> None: # noqa: B027
24
+ pass
25
+
26
+
27
+ class JsonFileExporter(AuditExporter):
28
+ def __init__(self, path: str | Path) -> None:
29
+ self._path = Path(path)
30
+ self._handle = open(self._path, "a") # noqa: SIM115
31
+
32
+ def export(self, entry: AuditEntry) -> None:
33
+ line = json.dumps(entry.to_dict(), default=str)
34
+ self._handle.write(line + "\n")
35
+
36
+ def flush(self) -> None:
37
+ self._handle.flush()
38
+
39
+ def shutdown(self) -> None:
40
+ self._handle.close()
41
+
42
+
43
+ def _require_opentelemetry() -> None:
44
+ try:
45
+ import opentelemetry # noqa: F401
46
+ except ImportError:
47
+ raise ImportError(
48
+ "opentelemetry is required for OtlpExporter. "
49
+ "Install it with: pip install gateframe[otlp]"
50
+ ) from None
51
+
52
+
53
+ class OtlpExporter(AuditExporter):
54
+ def __init__(
55
+ self,
56
+ endpoint: str = "http://localhost:4317",
57
+ *,
58
+ insecure: bool = True,
59
+ service_name: str = "gateframe",
60
+ ) -> None:
61
+ _require_opentelemetry()
62
+
63
+ from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter
64
+ from opentelemetry.sdk._logs import LoggerProvider
65
+ from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
66
+ from opentelemetry.sdk.resources import Resource
67
+
68
+ resource = Resource.create({"service.name": service_name})
69
+ self._provider = LoggerProvider(resource=resource)
70
+ exporter = OTLPLogExporter(endpoint=endpoint, insecure=insecure)
71
+ self._provider.add_log_record_processor(BatchLogRecordProcessor(exporter))
72
+ self._logger = self._provider.get_logger("gateframe.audit")
73
+
74
+ def export(self, entry: AuditEntry) -> None:
75
+ from opentelemetry.sdk._logs import LogRecord
76
+
77
+ severity = self._resolve_severity(entry)
78
+ attributes: dict[str, Any] = {
79
+ "contract_name": entry.contract_name,
80
+ "passed": entry.passed,
81
+ "rules_applied": entry.rules_applied,
82
+ "rules_failed": entry.rules_failed,
83
+ }
84
+ if entry.workflow_id is not None:
85
+ attributes["workflow_id"] = entry.workflow_id
86
+ if entry.confidence is not None:
87
+ attributes["confidence"] = entry.confidence
88
+
89
+ record = LogRecord(
90
+ body=json.dumps(entry.to_dict(), default=str),
91
+ severity_number=severity,
92
+ attributes=attributes,
93
+ )
94
+ self._logger.emit(record)
95
+
96
+ def flush(self) -> None:
97
+ self._provider.force_flush()
98
+
99
+ def shutdown(self) -> None:
100
+ self._provider.shutdown()
101
+
102
+ @staticmethod
103
+ def _resolve_severity(entry: AuditEntry) -> int:
104
+ import logging
105
+
106
+ has_hard = any(f.get("failure_mode") == "hard_fail" for f in entry.failures)
107
+ has_soft = any(f.get("failure_mode") == "soft_fail" for f in entry.failures)
108
+ if has_hard:
109
+ return logging.ERROR
110
+ if has_soft:
111
+ return logging.WARNING
112
+ return logging.INFO