dtaifm 0.1.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.
- dtaifm-0.1.0/LICENSE +21 -0
- dtaifm-0.1.0/PKG-INFO +597 -0
- dtaifm-0.1.0/README.md +560 -0
- dtaifm-0.1.0/dtaifm/__init__.py +23 -0
- dtaifm-0.1.0/dtaifm/__main__.py +9 -0
- dtaifm-0.1.0/dtaifm/_demo/__init__.py +0 -0
- dtaifm-0.1.0/dtaifm/_demo/network_automation/__init__.py +0 -0
- dtaifm-0.1.0/dtaifm/_demo/network_automation/constraints.yaml +48 -0
- dtaifm-0.1.0/dtaifm/_demo/network_automation/state.json +10 -0
- dtaifm-0.1.0/dtaifm/_demo/smart_home/__init__.py +0 -0
- dtaifm-0.1.0/dtaifm/_demo/smart_home/constraints.yaml +45 -0
- dtaifm-0.1.0/dtaifm/_demo/smart_home/state.json +13 -0
- dtaifm-0.1.0/dtaifm/audit.py +229 -0
- dtaifm-0.1.0/dtaifm/bundle.py +481 -0
- dtaifm-0.1.0/dtaifm/cli.py +846 -0
- dtaifm-0.1.0/dtaifm/core/__init__.py +19 -0
- dtaifm-0.1.0/dtaifm/core/constraint.py +38 -0
- dtaifm-0.1.0/dtaifm/core/result.py +55 -0
- dtaifm-0.1.0/dtaifm/core/rule.py +69 -0
- dtaifm-0.1.0/dtaifm/core/ruleset.py +17 -0
- dtaifm-0.1.0/dtaifm/domains/__init__.py +23 -0
- dtaifm-0.1.0/dtaifm/domains/base.py +33 -0
- dtaifm-0.1.0/dtaifm/domains/network_automation/__init__.py +46 -0
- dtaifm-0.1.0/dtaifm/domains/network_automation/evaluators.py +73 -0
- dtaifm-0.1.0/dtaifm/domains/registry.py +35 -0
- dtaifm-0.1.0/dtaifm/domains/smart_home/__init__.py +38 -0
- dtaifm-0.1.0/dtaifm/io.py +84 -0
- dtaifm-0.1.0/dtaifm/runtimes/__init__.py +3 -0
- dtaifm-0.1.0/dtaifm/runtimes/python_runtime.py +123 -0
- dtaifm-0.1.0/dtaifm/schema.py +168 -0
- dtaifm-0.1.0/dtaifm/serialize.py +41 -0
- dtaifm-0.1.0/dtaifm/student/__init__.py +3 -0
- dtaifm-0.1.0/dtaifm/student/validator.py +203 -0
- dtaifm-0.1.0/dtaifm/teacher/__init__.py +49 -0
- dtaifm-0.1.0/dtaifm/teacher/adapters/__init__.py +0 -0
- dtaifm-0.1.0/dtaifm/teacher/adapters/_http.py +59 -0
- dtaifm-0.1.0/dtaifm/teacher/adapters/anthropic_adapter.py +160 -0
- dtaifm-0.1.0/dtaifm/teacher/adapters/lemonade_adapter.py +92 -0
- dtaifm-0.1.0/dtaifm/teacher/adapters/ollama_adapter.py +89 -0
- dtaifm-0.1.0/dtaifm/teacher/base.py +23 -0
- dtaifm-0.1.0/dtaifm/teacher/contract.py +62 -0
- dtaifm-0.1.0/dtaifm/teacher/diagnostics.py +124 -0
- dtaifm-0.1.0/dtaifm/teacher/feedback.py +119 -0
- dtaifm-0.1.0/dtaifm/teacher/mock_teacher.py +183 -0
- dtaifm-0.1.0/dtaifm/teacher/parser.py +146 -0
- dtaifm-0.1.0/dtaifm/teacher/prompt.py +200 -0
- dtaifm-0.1.0/dtaifm/teacher/registry.py +77 -0
- dtaifm-0.1.0/dtaifm.egg-info/PKG-INFO +597 -0
- dtaifm-0.1.0/dtaifm.egg-info/SOURCES.txt +70 -0
- dtaifm-0.1.0/dtaifm.egg-info/dependency_links.txt +1 -0
- dtaifm-0.1.0/dtaifm.egg-info/entry_points.txt +2 -0
- dtaifm-0.1.0/dtaifm.egg-info/requires.txt +10 -0
- dtaifm-0.1.0/dtaifm.egg-info/top_level.txt +1 -0
- dtaifm-0.1.0/pyproject.toml +90 -0
- dtaifm-0.1.0/setup.cfg +4 -0
- dtaifm-0.1.0/tests/test_anthropic_adapter.py +163 -0
- dtaifm-0.1.0/tests/test_bundle.py +412 -0
- dtaifm-0.1.0/tests/test_cli.py +352 -0
- dtaifm-0.1.0/tests/test_custom_domain_template.py +116 -0
- dtaifm-0.1.0/tests/test_demo.py +260 -0
- dtaifm-0.1.0/tests/test_diagnostics.py +198 -0
- dtaifm-0.1.0/tests/test_domains.py +236 -0
- dtaifm-0.1.0/tests/test_feedback_repropose.py +534 -0
- dtaifm-0.1.0/tests/test_io.py +120 -0
- dtaifm-0.1.0/tests/test_local_adapters.py +392 -0
- dtaifm-0.1.0/tests/test_network_automation.py +257 -0
- dtaifm-0.1.0/tests/test_parser.py +195 -0
- dtaifm-0.1.0/tests/test_propose_review.py +217 -0
- dtaifm-0.1.0/tests/test_runtime.py +137 -0
- dtaifm-0.1.0/tests/test_schema.py +133 -0
- dtaifm-0.1.0/tests/test_teacher_contract.py +117 -0
- dtaifm-0.1.0/tests/test_validator.py +192 -0
dtaifm-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dtaifm contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
dtaifm-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dtaifm
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Deterministic-first Teaching AI Framework Middleware — AI proposes, the deterministic layer disposes.
|
|
5
|
+
Author: dtaifm contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/dtaifm/dtaifm
|
|
8
|
+
Project-URL: Repository, https://github.com/dtaifm/dtaifm
|
|
9
|
+
Project-URL: Issues, https://github.com/dtaifm/dtaifm/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/dtaifm/dtaifm/blob/main/CHANGELOG.md
|
|
11
|
+
Project-URL: Documentation, https://github.com/dtaifm/dtaifm/tree/main/docs
|
|
12
|
+
Keywords: ai,middleware,deterministic,validation,constraints,llm,ollama,anthropic,audit
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: System Administrators
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
24
|
+
Classifier: Topic :: System :: Systems Administration
|
|
25
|
+
Requires-Python: >=3.11
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: pyyaml>=6.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
31
|
+
Requires-Dist: jsonschema>=4.0; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
33
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
34
|
+
Provides-Extra: anthropic
|
|
35
|
+
Requires-Dist: anthropic>=0.30; extra == "anthropic"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# dtaifm — Deterministic-first Teaching AI Framework Middleware
|
|
39
|
+
|
|
40
|
+
[](https://github.com/dtaifm/dtaifm/actions/workflows/tests.yml)
|
|
41
|
+
[](https://www.python.org/downloads/)
|
|
42
|
+
[](LICENSE)
|
|
43
|
+
[](CHANGELOG.md)
|
|
44
|
+
[](tests/)
|
|
45
|
+
|
|
46
|
+
**AI proposes. The deterministic layer disposes. AI output is an artifact, not an action.**
|
|
47
|
+
|
|
48
|
+
dtaifm is open-source middleware for systems where AI generates candidate logic (rules, configurations, strategies) and a deterministic, constraint-verified layer has the final say. No AI output executes until it passes a human-defined constraint check.
|
|
49
|
+
|
|
50
|
+
> **What dtaifm is not.** dtaifm is **not** a smart-home product or a network-automation product. `smart_home` and `network_automation` are domain packs that ship in the box to demonstrate the pattern. The framework itself is provider-agnostic (mock, Anthropic, Ollama, Lemonade, bring-your-own) and domain-agnostic (bring your own — see [`examples/custom_domain_template/`](examples/custom_domain_template/)).
|
|
51
|
+
|
|
52
|
+
All file formats are explicitly versioned (`schema_version`) and have published JSON Schemas, so producers and consumers can evolve independently.
|
|
53
|
+
|
|
54
|
+
## 60-second demo
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install -e ".[dev]"
|
|
58
|
+
dtaifm demo smart_home
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
That single command walks the entire pipeline — `propose → validate → execute → bundle → replay` — and prints a step-by-step report ending in `RESULT: PASSED`. Runs fully offline using the mock teacher; no API key needed.
|
|
62
|
+
|
|
63
|
+
Want to see the second built-in domain or a local LLM driving it?
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
dtaifm demo network_automation # second built-in domain
|
|
67
|
+
dtaifm demo smart_home --teacher ollama --model llama3.2 # local model via Ollama
|
|
68
|
+
dtaifm demo smart_home --teacher lemonade --teacher-base-url http://192.0.2.10:13305 --model Qwen3-0.6B-GGUF
|
|
69
|
+
dtaifm demo smart_home --json # machine-readable
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The demo leaves the proposed rules, the audit bundle, and the hashes in a temp dir so you can `dtaifm inspect` / `dtaifm replay` them afterward.
|
|
73
|
+
|
|
74
|
+
## Documentation
|
|
75
|
+
|
|
76
|
+
| Doc | What it covers |
|
|
77
|
+
|---|---|
|
|
78
|
+
| [docs/launch.md](docs/launch.md) | The pitch, what's in v0.1, where it fits and does not fit |
|
|
79
|
+
| [docs/quickstart.md](docs/quickstart.md) | Install, run the demo, exercise every CLI command |
|
|
80
|
+
| [docs/concepts.md](docs/concepts.md) | Three-layer architecture, trust boundary, core primitives |
|
|
81
|
+
| [docs/domains.md](docs/domains.md) | Built-in domain packs + how to write your own |
|
|
82
|
+
| [docs/local-teachers.md](docs/local-teachers.md) | Ollama and Lemonade adapters, configuration, diagnostics |
|
|
83
|
+
| [docs/audit-bundles.md](docs/audit-bundles.md) | `.dtaifm-review.json`, replay, tamper detection |
|
|
84
|
+
| [docs/reproposal-loop.md](docs/reproposal-loop.md) | Validator → feedback → teacher revision cycle |
|
|
85
|
+
| [docs/comparison.md](docs/comparison.md) | Where dtaifm fits vs LLM agents, workflow engines, guardrails, policy engines, orchestration |
|
|
86
|
+
| [docs/roadmap.md](docs/roadmap.md) | What shipped in v0.1; near-term and longer-term plans |
|
|
87
|
+
| [docs/release-checklist.md](docs/release-checklist.md) | Maintainer pre-release checklist |
|
|
88
|
+
|
|
89
|
+
For contributors: [CONTRIBUTING.md](CONTRIBUTING.md), [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md), [SECURITY.md](SECURITY.md), [CHANGELOG.md](CHANGELOG.md).
|
|
90
|
+
|
|
91
|
+
## Why
|
|
92
|
+
|
|
93
|
+
Raw LLMs in production systems hallucinate. They are unpredictable at edge cases. Calling them on every user action is slow and expensive. dtaifm decouples the *intelligence* (the AI teacher) from the *execution* (the deterministic student) so you get AI-level optimization with deterministic-level safety and auditability.
|
|
94
|
+
|
|
95
|
+
## Architecture
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
Constraints (YAML) ← defined by humans; never changed by AI
|
|
99
|
+
│
|
|
100
|
+
▼
|
|
101
|
+
Teacher.propose_rules() ← AI model or mock; returns a candidate RuleSet
|
|
102
|
+
│
|
|
103
|
+
▼
|
|
104
|
+
Validator.validate_ruleset() ← deterministic; approves or rejects each rule
|
|
105
|
+
│
|
|
106
|
+
▼
|
|
107
|
+
PythonRuntime.fire() ← executes approved rules only; predictable, auditable
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Three layers, one contract: **the AI never executes anything directly.**
|
|
111
|
+
|
|
112
|
+
## Quickstart
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
git clone <repo-url>
|
|
116
|
+
cd dtaifm
|
|
117
|
+
pip install -e ".[dev]"
|
|
118
|
+
|
|
119
|
+
# Run the smart home demo (Python script)
|
|
120
|
+
python examples/smart_rules/demo.py
|
|
121
|
+
|
|
122
|
+
# Run the test suite
|
|
123
|
+
pytest
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## CLI
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Emit a JSON Schema for one of the portable file kinds
|
|
130
|
+
dtaifm schema constraints
|
|
131
|
+
dtaifm schema rules
|
|
132
|
+
dtaifm schema state
|
|
133
|
+
|
|
134
|
+
# Run a teacher and write a portable proposed rule file (does NOT validate or execute)
|
|
135
|
+
dtaifm propose examples/smart_rules/constraints.yaml --teacher mock --out proposed.yaml
|
|
136
|
+
|
|
137
|
+
# Audit a rule file against a constraint file (exit 1 if any rule is rejected)
|
|
138
|
+
dtaifm validate examples/smart_rules/constraints.yaml examples/smart_rules/rules.yaml
|
|
139
|
+
|
|
140
|
+
# Validate, then execute approved rules against a state event
|
|
141
|
+
dtaifm run examples/smart_rules/constraints.yaml examples/smart_rules/rules.yaml \
|
|
142
|
+
--state examples/smart_rules/state.json
|
|
143
|
+
|
|
144
|
+
# Combined audit: proposal metadata + validation + execution trace + final actions
|
|
145
|
+
dtaifm review examples/smart_rules/constraints.yaml proposed.yaml \
|
|
146
|
+
--state examples/smart_rules/state.json --json
|
|
147
|
+
|
|
148
|
+
# Write a portable audit bundle alongside the review
|
|
149
|
+
dtaifm review examples/smart_rules/constraints.yaml proposed.yaml \
|
|
150
|
+
--state examples/smart_rules/state.json --bundle review.json
|
|
151
|
+
|
|
152
|
+
# Replay a bundle and verify it reproduces exactly (exit 1 on mismatch)
|
|
153
|
+
dtaifm replay review.json
|
|
154
|
+
|
|
155
|
+
# Read-only summary of a bundle — no execution
|
|
156
|
+
dtaifm inspect review.json
|
|
157
|
+
|
|
158
|
+
# Build a deterministic feedback artifact from validation failures (NO execution)
|
|
159
|
+
dtaifm feedback constraints.yaml rules.yaml --out feedback.json
|
|
160
|
+
|
|
161
|
+
# Hand the deterministic feedback to a teacher and write a revised rule file
|
|
162
|
+
# (the revised file is NOT validated or executed by repropose — only review/validate does that)
|
|
163
|
+
dtaifm repropose constraints.yaml rules.yaml --teacher mock --out revised.yaml
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`--json` is available on `validate`, `run`, `review`, `replay`, and `inspect`. If the `dtaifm` entry point isn't on your PATH after install, use `python -m dtaifm ...` instead.
|
|
167
|
+
|
|
168
|
+
## Audit Bundles & Replay
|
|
169
|
+
|
|
170
|
+
`dtaifm review --bundle review.json` writes a self-contained audit artifact embedding the constraints, rules, state, validation result, and execution trace, with **SHA-256 hashes** over the canonical-JSON form of each. The bundle is portable across machines and survives YAML/JSON formatting differences.
|
|
171
|
+
|
|
172
|
+
`dtaifm replay review.json` re-executes the review on a fresh checkout and verifies:
|
|
173
|
+
|
|
174
|
+
1. Embedded inputs match their recorded hashes (catches input tampering).
|
|
175
|
+
2. Stored validation/execution results match their recorded hashes (catches result tampering).
|
|
176
|
+
3. Recomputed validation/execution from inputs match the stored hashes (catches framework non-determinism or domain semantic drift).
|
|
177
|
+
|
|
178
|
+
A domain-version mismatch becomes a **warning** rather than a failure when results still match (a non-breaking domain change). Replay never invokes a teacher or provider adapter — it's a pure deterministic verification.
|
|
179
|
+
|
|
180
|
+
### Public Python API
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from dtaifm import review, replay, inspect_bundle
|
|
184
|
+
|
|
185
|
+
bundle = review(
|
|
186
|
+
constraints_path="constraints.yaml",
|
|
187
|
+
rules_path="rules.yaml",
|
|
188
|
+
state_path="state.json",
|
|
189
|
+
domain_id="smart_home",
|
|
190
|
+
bundle_path="review.json", # optional
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
result = replay("review.json") # or replay(bundle)
|
|
194
|
+
assert result.success
|
|
195
|
+
assert result.inputs_intact
|
|
196
|
+
assert result.validation_matches
|
|
197
|
+
|
|
198
|
+
summary = inspect_bundle("review.json") # pure read; no execution
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The CLI is a thin wrapper over these three functions.
|
|
202
|
+
|
|
203
|
+
### Pipeline
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
propose -> validate -> run (or `review` for all three in one report)
|
|
207
|
+
[teacher] [student] [runtime]
|
|
208
|
+
artifact gate execution
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
`propose` only writes a file. `validate` only audits it. The runtime only ever sees rules that the validator approved. The principle: AI output is an artifact, not an action.
|
|
212
|
+
|
|
213
|
+
### File formats (all versioned)
|
|
214
|
+
|
|
215
|
+
Every dtaifm file carries `schema_version: "0.1"` at the top level. Loaders reject files with a missing or unsupported version.
|
|
216
|
+
|
|
217
|
+
**constraints.yaml**
|
|
218
|
+
|
|
219
|
+
```yaml
|
|
220
|
+
schema_version: "0.1"
|
|
221
|
+
constraints:
|
|
222
|
+
- id: no_auto_unlock
|
|
223
|
+
description: "Never unlock doors automatically."
|
|
224
|
+
type: absolute_prohibition
|
|
225
|
+
applies_to: [front_door]
|
|
226
|
+
action: unlock
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**rules.yaml** (proposed rules carry provenance; hand-written rules may omit it)
|
|
230
|
+
|
|
231
|
+
```yaml
|
|
232
|
+
schema_version: "0.1"
|
|
233
|
+
rules:
|
|
234
|
+
- id: r_x
|
|
235
|
+
name: "Example"
|
|
236
|
+
trigger: { device: motion_sensor, event: motion_detected }
|
|
237
|
+
conditions: []
|
|
238
|
+
actions:
|
|
239
|
+
- { device: hallway_light, action: turn_on }
|
|
240
|
+
satisfies_constraints: [motion_light_hours]
|
|
241
|
+
explanation: "What the rule does."
|
|
242
|
+
rationale: "Why the teacher proposed it."
|
|
243
|
+
proposed_by: mock
|
|
244
|
+
proposal_id: 7f3c...
|
|
245
|
+
created_at: "2026-05-24T10:00:00+00:00"
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**state.json**
|
|
249
|
+
|
|
250
|
+
```json
|
|
251
|
+
{
|
|
252
|
+
"schema_version": "0.1",
|
|
253
|
+
"event": { "device": "motion_sensor", "type": "motion_detected" },
|
|
254
|
+
"time": "2024-01-01T23:00:00",
|
|
255
|
+
"mode": "normal",
|
|
256
|
+
"devices": { "ac": "off", "heating": "off" }
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Execution trace
|
|
261
|
+
|
|
262
|
+
Every `run` produces a deterministic trace explaining why each approved rule fired or was skipped — the condition that failed, with its evaluated parameters. Rejected rules never reach the runtime and therefore never appear in the trace.
|
|
263
|
+
|
|
264
|
+
```
|
|
265
|
+
FIRED [r_motion_night_light] trigger matched and all conditions passed
|
|
266
|
+
- time_range {'start_hour': 22, 'end_hour': 6} -> ok
|
|
267
|
+
- mode_not {'mode': 'security'} -> ok
|
|
268
|
+
SKIPPED [r_heating_cold] trigger did not match (rule expects thermostat.temperature_below_threshold)
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Expected demo output
|
|
272
|
+
|
|
273
|
+
```
|
|
274
|
+
=== dtaifm Smart Home Demo ===
|
|
275
|
+
AI proposes. The deterministic layer disposes.
|
|
276
|
+
|
|
277
|
+
──────────────────────────────────────────────────
|
|
278
|
+
1. Constraints (defined by humans)
|
|
279
|
+
──────────────────────────────────────────────────
|
|
280
|
+
[no_auto_unlock] Never unlock doors automatically.
|
|
281
|
+
[no_hvac_conflict] Do not turn heating and AC on at the same time.
|
|
282
|
+
[motion_light_hours] Lights may turn on from motion only during configured night hours.
|
|
283
|
+
[security_override] Security mode overrides comfort automation.
|
|
284
|
+
[rule_must_explain] Every generated rule must explain which constraint it satisfies.
|
|
285
|
+
|
|
286
|
+
──────────────────────────────────────────────────
|
|
287
|
+
2. Teacher proposes rules (AI / mock)
|
|
288
|
+
──────────────────────────────────────────────────
|
|
289
|
+
3 rule(s) proposed:
|
|
290
|
+
[r_motion_night_light] Motion-Activated Night Light
|
|
291
|
+
[r_auto_unlock_door] Auto-Unlock on Arrival (UNSAFE)
|
|
292
|
+
[r_heating_cold] Activate Heating When Cold
|
|
293
|
+
|
|
294
|
+
──────────────────────────────────────────────────
|
|
295
|
+
3. Validator reviews each rule (deterministic)
|
|
296
|
+
──────────────────────────────────────────────────
|
|
297
|
+
APPROVED [r_motion_night_light] Motion-Activated Night Light
|
|
298
|
+
REJECTED [r_auto_unlock_door] Auto-Unlock on Arrival (UNSAFE)
|
|
299
|
+
! [no_auto_unlock] Rule 'r_auto_unlock_door' performs prohibited action 'unlock' on device 'front_door'.
|
|
300
|
+
! [rule_must_explain] Rule 'r_auto_unlock_door' does not declare which constraints it satisfies.
|
|
301
|
+
APPROVED [r_heating_cold] Activate Heating When Cold
|
|
302
|
+
|
|
303
|
+
──────────────────────────────────────────────────
|
|
304
|
+
4. Runtime executes approved rules (deterministic)
|
|
305
|
+
──────────────────────────────────────────────────
|
|
306
|
+
|
|
307
|
+
Event: motion_detected at 23:00 — normal mode
|
|
308
|
+
-> [r_motion_night_light] hallway_light: turn_on {'duration': 300}
|
|
309
|
+
|
|
310
|
+
Event: motion_detected at 23:00 — security mode
|
|
311
|
+
-> (no rules triggered)
|
|
312
|
+
|
|
313
|
+
Event: motion_detected at 14:00 — normal mode (outside night hours)
|
|
314
|
+
-> (no rules triggered)
|
|
315
|
+
|
|
316
|
+
Event: temperature_below_threshold — AC off, normal mode
|
|
317
|
+
-> [r_heating_cold] heating: turn_on
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Core Concepts
|
|
321
|
+
|
|
322
|
+
| Concept | Role |
|
|
323
|
+
|---|---|
|
|
324
|
+
| `Constraint` | A hard rule a system must never violate. Defined by humans in YAML. Immutable at runtime. |
|
|
325
|
+
| `Rule` | A candidate action proposed by the AI teacher. Contains trigger, conditions, actions, and a declaration of which constraints it satisfies. |
|
|
326
|
+
| `RuleSet` | A collection of proposed rules returned by a single teacher call. |
|
|
327
|
+
| `ValidationResult` | The outcome of checking one Rule against all Constraints. Carries violations with reasons. |
|
|
328
|
+
| `ExecutionResult` | The outcome of firing approved rules against a live system state event. |
|
|
329
|
+
|
|
330
|
+
### Constraint types
|
|
331
|
+
|
|
332
|
+
| Type | Description |
|
|
333
|
+
|---|---|
|
|
334
|
+
| `absolute_prohibition` | A specific action on a specific device is never allowed. |
|
|
335
|
+
| `mutual_exclusion` | Two or more devices must never be activated simultaneously. |
|
|
336
|
+
| `temporal_restriction` | A device may only be controlled via a trigger within a time window. |
|
|
337
|
+
| `mode_override` | A named mode (e.g. `security`) supersedes comfort automation. |
|
|
338
|
+
| `metadata_requirement` | Every rule must carry specified metadata fields. |
|
|
339
|
+
|
|
340
|
+
## Defining Your Own Constraints
|
|
341
|
+
|
|
342
|
+
```yaml
|
|
343
|
+
# constraints.yaml
|
|
344
|
+
constraints:
|
|
345
|
+
- id: no_auto_unlock
|
|
346
|
+
description: "Never unlock doors automatically."
|
|
347
|
+
type: absolute_prohibition
|
|
348
|
+
applies_to:
|
|
349
|
+
- front_door
|
|
350
|
+
action: unlock
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Load them in Python:
|
|
354
|
+
|
|
355
|
+
```python
|
|
356
|
+
import yaml
|
|
357
|
+
from dtaifm.core.constraint import Constraint
|
|
358
|
+
|
|
359
|
+
with open("constraints.yaml") as f:
|
|
360
|
+
data = yaml.safe_load(f)
|
|
361
|
+
constraints = [Constraint.from_dict(c) for c in data["constraints"]]
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
## Domains
|
|
365
|
+
|
|
366
|
+
dtaifm is **middleware**, not a smart-home engine. A *domain pack* declares what is possible in a given system — its allowed trigger events, condition types, action verbs, and any domain-specific constraint evaluators. Teachers propose only within that boundary; the validator and runtime both refuse out-of-vocabulary rules.
|
|
367
|
+
|
|
368
|
+
Two domains ship in the box:
|
|
369
|
+
|
|
370
|
+
| Domain id | What it covers |
|
|
371
|
+
|---|---|
|
|
372
|
+
| `smart_home` (default) | Residential automation: lights, HVAC, locks, sensors. |
|
|
373
|
+
| `network_automation` | Router/switch config, BGP, maintenance windows; includes custom evaluators for `companion_action_required`, `action_target_limit`, `mode_required`. |
|
|
374
|
+
|
|
375
|
+
Every CLI command takes `--domain`:
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
# smart_home (default)
|
|
379
|
+
dtaifm validate examples/smart_rules/constraints.yaml examples/smart_rules/rules.yaml
|
|
380
|
+
|
|
381
|
+
# network_automation
|
|
382
|
+
dtaifm validate --domain network_automation \
|
|
383
|
+
examples/network_automation/constraints.yaml examples/network_automation/rules.yaml
|
|
384
|
+
|
|
385
|
+
dtaifm review --domain network_automation \
|
|
386
|
+
examples/network_automation/constraints.yaml examples/network_automation/rules.yaml \
|
|
387
|
+
--state examples/network_automation/state.json
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Adding your own domain
|
|
391
|
+
|
|
392
|
+
```python
|
|
393
|
+
from dtaifm.domains.base import Domain
|
|
394
|
+
from dtaifm.domains.registry import register_domain
|
|
395
|
+
|
|
396
|
+
MY_DOMAIN = Domain(
|
|
397
|
+
id="my_domain",
|
|
398
|
+
version="0.1",
|
|
399
|
+
trigger_events=frozenset({"order_placed", "shipment_delayed"}),
|
|
400
|
+
condition_types=frozenset({"time_range", "mode_not", "device_state"}),
|
|
401
|
+
action_kinds=frozenset({"notify", "refund", "escalate"}),
|
|
402
|
+
extra_constraint_evaluators={
|
|
403
|
+
# "my_custom_type": my_evaluator, # (rule, constraint) -> ConstraintViolation | None
|
|
404
|
+
},
|
|
405
|
+
)
|
|
406
|
+
register_domain(MY_DOMAIN)
|
|
407
|
+
# Now: dtaifm validate --domain my_domain ...
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Provider adapters (Anthropic, OpenAI, etc.) contain **no domain logic**. They are translators that take a `TeacherRequest` (which carries the domain) and return a portable RuleSet. The domain's vocabulary is rendered into the prompt automatically.
|
|
411
|
+
|
|
412
|
+
## Teachers
|
|
413
|
+
|
|
414
|
+
A **Teacher** translates a `TeacherRequest` (constraints + context) into a `TeacherResponse` carrying a portable `RuleSet` artifact. Teachers never validate or execute. **Provider adapters are translators, not trusted components.**
|
|
415
|
+
|
|
416
|
+
### Mock teacher (always available, no install needed)
|
|
417
|
+
|
|
418
|
+
```bash
|
|
419
|
+
dtaifm propose constraints.yaml --teacher mock --out proposed.yaml
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### Anthropic Claude adapter (optional extra)
|
|
423
|
+
|
|
424
|
+
```bash
|
|
425
|
+
pip install 'dtaifm[anthropic]'
|
|
426
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
427
|
+
|
|
428
|
+
# (optional) override the default model:
|
|
429
|
+
export ANTHROPIC_MODEL=claude-opus-4-7
|
|
430
|
+
|
|
431
|
+
dtaifm propose constraints.yaml --teacher anthropic --domain smart_home --out proposed.yaml
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
The Anthropic SDK is **not** a core dependency. `pip install dtaifm` still works without it; an attempt to use `--teacher anthropic` without the extra installed fails with a clear install hint.
|
|
435
|
+
|
|
436
|
+
### Local teachers — Ollama and Lemonade (no API keys)
|
|
437
|
+
|
|
438
|
+
Both adapters speak plain JSON over HTTP via stdlib — no extra install required. Defaults:
|
|
439
|
+
|
|
440
|
+
| Teacher | Default base URL | Endpoint | Env var |
|
|
441
|
+
|---|---|---|---|
|
|
442
|
+
| `ollama` | `http://localhost:11434` | `POST /api/chat` | `DTAIFM_OLLAMA_BASE_URL` |
|
|
443
|
+
| `lemonade` | `http://localhost:13305` | `POST /v1/chat/completions` (OpenAI-compatible) | `DTAIFM_LEMONADE_BASE_URL` |
|
|
444
|
+
|
|
445
|
+
Override precedence is **CLI flag > env var > default**, and trailing slashes are normalized.
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
# Local Ollama (default endpoint, llama3.2 by default)
|
|
449
|
+
dtaifm propose constraints.yaml --teacher ollama --domain smart_home --out proposed.yaml
|
|
450
|
+
|
|
451
|
+
# Local Ollama with a specific model
|
|
452
|
+
dtaifm propose constraints.yaml --teacher ollama --model qwen3:0.6b --out proposed.yaml
|
|
453
|
+
|
|
454
|
+
# Lemonade on a remote workstation
|
|
455
|
+
dtaifm propose constraints.yaml \
|
|
456
|
+
--teacher lemonade \
|
|
457
|
+
--teacher-base-url http://192.0.2.10:13305 \
|
|
458
|
+
--model Qwen3-0.6B-GGUF \
|
|
459
|
+
--out proposed.yaml
|
|
460
|
+
|
|
461
|
+
# Or, via env var:
|
|
462
|
+
export DTAIFM_LEMONADE_BASE_URL=http://192.0.2.10:13305
|
|
463
|
+
dtaifm propose constraints.yaml --teacher lemonade --model Qwen3-0.6B-GGUF --out proposed.yaml
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
The local adapters route the model's response through the same strict parser as the Anthropic adapter — malformed output fails clearly, narration outside the JSON block is tolerated, and provenance fields (`rationale`, `satisfies_constraints`) are required. **Local models improve privacy and adoption, but they are still untrusted teachers** — every proposed rule still has to pass `dtaifm review` before anything executes.
|
|
467
|
+
|
|
468
|
+
### Diagnosing your local setup
|
|
469
|
+
|
|
470
|
+
```bash
|
|
471
|
+
dtaifm teachers # list registered teachers + base URLs + env-var hints
|
|
472
|
+
dtaifm teachers --check # additionally ping local endpoints; offline servers are reported gracefully
|
|
473
|
+
dtaifm teachers --json # machine-readable
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
## Reproposal Loop
|
|
477
|
+
|
|
478
|
+
Teachers rarely produce a perfect first proposal. The reproposal loop lets any teacher (mock, Anthropic, Ollama, Lemonade) consume the validator's deterministic violation reasons and try again — without weakening the trust boundary.
|
|
479
|
+
|
|
480
|
+
```bash
|
|
481
|
+
# 1. The teacher's first attempt
|
|
482
|
+
dtaifm propose constraints.yaml --teacher ollama --out v1.yaml
|
|
483
|
+
|
|
484
|
+
# 2. Inspect what failed (validator only — no execution)
|
|
485
|
+
dtaifm feedback constraints.yaml v1.yaml --out feedback.json
|
|
486
|
+
# feedback.json contains: schema_version, domain, rejected_rules
|
|
487
|
+
# [{rule_id, name, violations, allowed_triggers, allowed_conditions, allowed_actions}]
|
|
488
|
+
|
|
489
|
+
# 3. Repropose: the teacher receives the previous rules + the named violations
|
|
490
|
+
dtaifm repropose constraints.yaml v1.yaml --teacher ollama --out v2.yaml
|
|
491
|
+
|
|
492
|
+
# 4. Now run a real review on the revised file (THIS is where execution happens)
|
|
493
|
+
dtaifm review constraints.yaml v2.yaml --state state.json --bundle review.json
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
**Guarantees enforced in code:**
|
|
497
|
+
|
|
498
|
+
- `dtaifm feedback` never instantiates the runtime (tests assert this).
|
|
499
|
+
- `dtaifm repropose` validates the original rules once to build feedback, calls the teacher, and writes the revised file — it does **not** validate or execute the revision. Even if the teacher returns an unsafe rule, repropose writes it. Only `dtaifm review` or `dtaifm validate` gates execution.
|
|
500
|
+
- The prompt's `REVISION REQUESTED` section uses stable, grep-able markers (`YOUR PREVIOUS RULES:`, `REJECTED RULES (must be fixed or removed):`, `Violations:`, `Allowed triggers:` / `conditions:` / `actions:`) so adapter tests can lock its format.
|
|
501
|
+
|
|
502
|
+
The principle: **the deterministic layer may teach the teacher, but it never lets the teacher grade itself.**
|
|
503
|
+
|
|
504
|
+
> **Warning:** Generated rules are an artifact, not a green light. Always run them through `dtaifm review` (or `dtaifm validate` + `dtaifm run`) before deploying — the validator is the only gate that authorizes execution.
|
|
505
|
+
|
|
506
|
+
### Inspecting the prompt
|
|
507
|
+
|
|
508
|
+
```bash
|
|
509
|
+
dtaifm prompt constraints.yaml --teacher anthropic --domain smart_home
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
`dtaifm prompt` shows the exact text input the adapter would send. It requires no API key and makes no network calls.
|
|
513
|
+
|
|
514
|
+
### Building your own teacher
|
|
515
|
+
|
|
516
|
+
```python
|
|
517
|
+
from dtaifm.teacher.base import Teacher
|
|
518
|
+
from dtaifm.teacher.contract import TeacherRequest, TeacherResponse
|
|
519
|
+
from dtaifm.teacher.parser import parse_provider_payload
|
|
520
|
+
from dtaifm.teacher.registry import register_teacher
|
|
521
|
+
|
|
522
|
+
class CustomTeacher(Teacher):
|
|
523
|
+
def propose(self, request: TeacherRequest) -> TeacherResponse:
|
|
524
|
+
# 1. self.render_prompt(request) gives you the standard prompt
|
|
525
|
+
# 2. Call your provider, request JSON output that matches dtaifm.schema.RULES_SCHEMA
|
|
526
|
+
# 3. Run the response through parse_provider_payload for strict validation
|
|
527
|
+
payload = ... # dict from your provider
|
|
528
|
+
ruleset = parse_provider_payload(payload, source="custom")
|
|
529
|
+
return TeacherResponse(ruleset=ruleset, raw_provider_output=...)
|
|
530
|
+
|
|
531
|
+
register_teacher("custom", CustomTeacher)
|
|
532
|
+
# Now: dtaifm propose constraints.yaml --teacher custom --out proposed.yaml
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
`dtaifm propose` stamps `proposed_by`, `proposal_id`, and `created_at` on every rule automatically. Teachers only need to supply the rule logic and a non-empty `rationale` for each rule.
|
|
536
|
+
|
|
537
|
+
## Developer commands
|
|
538
|
+
|
|
539
|
+
```bash
|
|
540
|
+
# Install with dev tools (pytest, jsonschema, ruff, build)
|
|
541
|
+
pip install -e ".[dev]"
|
|
542
|
+
|
|
543
|
+
# Run the test suite (256 tests, fully offline, no API keys)
|
|
544
|
+
pytest
|
|
545
|
+
|
|
546
|
+
# Lint and format
|
|
547
|
+
ruff check dtaifm tests
|
|
548
|
+
ruff format dtaifm tests
|
|
549
|
+
|
|
550
|
+
# Build a wheel
|
|
551
|
+
python -m build --wheel
|
|
552
|
+
|
|
553
|
+
# Smoke-test the wheel in a clean venv (also runs in CI)
|
|
554
|
+
python -m venv /tmp/wheel-test
|
|
555
|
+
/tmp/wheel-test/bin/pip install dist/dtaifm-0.1.0-py3-none-any.whl
|
|
556
|
+
/tmp/wheel-test/bin/dtaifm --help
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
Optional type checking (`mypy dtaifm`) is supported but not enforced in CI.
|
|
560
|
+
|
|
561
|
+
## Roadmap
|
|
562
|
+
|
|
563
|
+
- [x] CLI (`validate`, `run`) with text + JSON output
|
|
564
|
+
- [x] Portable rule files (YAML / JSON)
|
|
565
|
+
- [x] Deterministic execution trace
|
|
566
|
+
- [x] CI on Python 3.11–3.13 (core install + anthropic-extra install)
|
|
567
|
+
- [x] Schema versioning + published JSON Schemas (`dtaifm schema`)
|
|
568
|
+
- [x] `dtaifm propose` (teacher artifact) and `dtaifm review` (combined audit)
|
|
569
|
+
- [x] Rule provenance fields (`proposed_by`, `proposal_id`, `created_at`, `rationale`)
|
|
570
|
+
- [x] Teacher registry (adapter slot — no provider deps in core)
|
|
571
|
+
- [x] `TeacherRequest` / `TeacherResponse` / `PromptContext` provider-neutral contract
|
|
572
|
+
- [x] `dtaifm prompt` — inspect adapter input with no API key
|
|
573
|
+
- [x] Strict provider response parsing (`ProviderResponseError`)
|
|
574
|
+
- [x] Anthropic Claude teacher adapter (optional extra)
|
|
575
|
+
- [x] Domain pack abstraction with registry (`smart_home`, `network_automation`)
|
|
576
|
+
- [x] Domain-aware validator (rejects out-of-vocabulary triggers/conditions/actions)
|
|
577
|
+
- [x] Runtime defense-in-depth: refuses actions outside the active domain
|
|
578
|
+
- [x] Domain-aware prompts (every adapter receives the domain's vocabulary)
|
|
579
|
+
- [x] Replayable audit bundles (`.dtaifm-review.json`) with canonical-JSON SHA-256 hashes
|
|
580
|
+
- [x] `dtaifm replay` and `dtaifm inspect` commands; public Python API (`review`, `replay`, `inspect_bundle`)
|
|
581
|
+
- [x] Tamper detection across inputs, stored results, and recomputed results
|
|
582
|
+
- [x] Local teacher adapters: `ollama` and `lemonade` (no API keys, no extra deps)
|
|
583
|
+
- [x] `dtaifm teachers` / `dtaifm teachers --check` connectivity diagnostics
|
|
584
|
+
- [x] Deterministic feedback artifacts (`dtaifm feedback`) — validation-only, no execution
|
|
585
|
+
- [x] Reproposal loop (`dtaifm repropose`) — teachers consume named violations through the same `TeacherRequest` contract; the revision is written but not validated/executed
|
|
586
|
+
- [ ] OpenAI teacher adapter (optional extra)
|
|
587
|
+
- [ ] Persistent audit log of every propose → validate → execute cycle
|
|
588
|
+
- [ ] Telecom / network automation example
|
|
589
|
+
- [ ] Rust/WASM deterministic runtime
|
|
590
|
+
|
|
591
|
+
## Contributing
|
|
592
|
+
|
|
593
|
+
Contributions welcome. The most valuable areas right now are teacher adapters for real AI providers and additional constraint types. Open an issue before large PRs.
|
|
594
|
+
|
|
595
|
+
## License
|
|
596
|
+
|
|
597
|
+
MIT
|