{"id":50400042,"url":"https://github.com/dtaifm/dtaifm","last_synced_at":"2026-05-30T23:00:29.876Z","repository":{"id":359957075,"uuid":"1248111819","full_name":"dtaifm/dtaifm","owner":"dtaifm","description":"Deterministic-first AI middleware. AI proposes; the deterministic layer disposes. AI output is an artifact, not an action.","archived":false,"fork":false,"pushed_at":"2026-05-24T10:30:56.000Z","size":141,"stargazers_count":0,"open_issues_count":7,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-24T11:12:19.025Z","etag":null,"topics":["ai","anthropic","audit","constraint-validation","deterministic","guardrails","lemonade","llm","middleware","ollama","python"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/dtaifm/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dtaifm.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":"docs/roadmap.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-24T07:44:15.000Z","updated_at":"2026-05-24T10:30:56.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/dtaifm/dtaifm","commit_stats":null,"previous_names":["dtaifm/dtaifm"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/dtaifm/dtaifm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dtaifm%2Fdtaifm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dtaifm%2Fdtaifm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dtaifm%2Fdtaifm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dtaifm%2Fdtaifm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dtaifm","download_url":"https://codeload.github.com/dtaifm/dtaifm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dtaifm%2Fdtaifm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33712579,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","anthropic","audit","constraint-validation","deterministic","guardrails","lemonade","llm","middleware","ollama","python"],"created_at":"2026-05-30T23:00:29.086Z","updated_at":"2026-05-30T23:00:29.847Z","avatar_url":"https://github.com/dtaifm.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dtaifm — Deterministic-first Teaching AI Framework Middleware\n\n[![tests](https://github.com/dtaifm/dtaifm/actions/workflows/tests.yml/badge.svg)](https://github.com/dtaifm/dtaifm/actions/workflows/tests.yml)\n[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![Version](https://img.shields.io/badge/version-0.1.3-blue.svg)](CHANGELOG.md)\n[![Tests passing](https://img.shields.io/badge/tests-324%20passing-brightgreen.svg)](tests/)\n\n**AI proposes. The deterministic layer disposes. AI output is an artifact, not an action.**\n\ndtaifm 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.\n\n\u003e **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, OpenAI, Ollama, Lemonade, bring-your-own) and domain-agnostic (bring your own — see [`examples/custom_domain_template/`](examples/custom_domain_template/)).\n\nAll file formats are explicitly versioned (`schema_version`) and have published JSON Schemas, so producers and consumers can evolve independently.\n\n![dtaifm demo — Qwen 3.6 via Lemonade on the network_automation domain](docs/assets/dtaifm-demo.png)\n\n*A real local run: Qwen 3.6 via Lemonade proposes network-automation rules; dtaifm approves only the rule that passes deterministic validation, rejects the unsafe proposals with named violations (`changes_in_maintenance_mode_only`), writes an audit bundle, and verifies replay end-to-end.*\n\n## 60-second demo\n\n```bash\npip install dtaifm\ndtaifm demo smart_home\n```\n\nThat 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.\n\n\u003e **Alternate source install** — if you want to develop against the latest `main`, install from GitHub instead: `pip install git+https://github.com/dtaifm/dtaifm.git`. For pinned source installs, append `@v0.1.0` (or any tag).\n\nWant to see the second built-in domain or a local LLM driving it?\n\n```bash\ndtaifm demo network_automation                                  # second built-in domain\ndtaifm demo smart_home --teacher ollama --model llama3.2        # local model via Ollama\ndtaifm demo smart_home --teacher lemonade --teacher-base-url http://192.0.2.10:13305 --model Qwen3-0.6B-GGUF\ndtaifm demo smart_home --json                                   # machine-readable\n```\n\nThe demo leaves the proposed rules, the audit bundle, and the hashes in a temp dir so you can `dtaifm inspect` / `dtaifm replay` them afterward.\n\n## Documentation\n\n| Doc | What it covers |\n|---|---|\n| [docs/launch.md](docs/launch.md) | The pitch, what's in v0.1, where it fits and does not fit |\n| [docs/quickstart.md](docs/quickstart.md) | Install, run the demo, exercise every CLI command |\n| [docs/concepts.md](docs/concepts.md) | Three-layer architecture, trust boundary, core primitives |\n| [docs/domains.md](docs/domains.md) | Built-in domain packs + how to write your own |\n| [docs/local-teachers.md](docs/local-teachers.md) | Ollama and Lemonade adapters, configuration, diagnostics |\n| [docs/audit-bundles.md](docs/audit-bundles.md) | `.dtaifm-review.json`, replay, tamper detection |\n| [docs/reproposal-loop.md](docs/reproposal-loop.md) | Validator → feedback → teacher revision cycle |\n| [docs/comparison.md](docs/comparison.md) | Where dtaifm fits vs LLM agents, workflow engines, guardrails, policy engines, orchestration |\n| [docs/roadmap.md](docs/roadmap.md) | What shipped in v0.1; near-term and longer-term plans |\n| [docs/release-checklist.md](docs/release-checklist.md) | Maintainer pre-release checklist |\n\nFor contributors: [CONTRIBUTING.md](CONTRIBUTING.md), [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md), [SECURITY.md](SECURITY.md), [CHANGELOG.md](CHANGELOG.md).\n\n## Why\n\nRaw 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.\n\n## Architecture\n\n```\n  Constraints (YAML)           ← defined by humans; never changed by AI\n        │\n        ▼\n  Teacher.propose_rules()      ← AI model or mock; returns a candidate RuleSet\n        │\n        ▼\n  Validator.validate_ruleset() ← deterministic; approves or rejects each rule\n        │\n        ▼\n  PythonRuntime.fire()         ← executes approved rules only; predictable, auditable\n```\n\nThree layers, one contract: **the AI never executes anything directly.**\n\n## Quickstart\n\n```bash\ngit clone \u003crepo-url\u003e\ncd dtaifm\npip install -e \".[dev]\"\n\n# Run the smart home demo (Python script)\npython examples/smart_rules/demo.py\n\n# Run the test suite\npytest\n```\n\n## CLI\n\n```bash\n# Emit a JSON Schema for one of the portable file kinds\ndtaifm schema constraints\ndtaifm schema rules\ndtaifm schema state\n\n# Run a teacher and write a portable proposed rule file (does NOT validate or execute)\ndtaifm propose examples/smart_rules/constraints.yaml --teacher mock --out proposed.yaml\n\n# Audit a rule file against a constraint file (exit 1 if any rule is rejected)\ndtaifm validate examples/smart_rules/constraints.yaml examples/smart_rules/rules.yaml\n\n# Validate, then execute approved rules against a state event\ndtaifm run examples/smart_rules/constraints.yaml examples/smart_rules/rules.yaml \\\n  --state examples/smart_rules/state.json\n\n# Combined audit: proposal metadata + validation + execution trace + final actions\ndtaifm review examples/smart_rules/constraints.yaml proposed.yaml \\\n  --state examples/smart_rules/state.json --json\n\n# Write a portable audit bundle alongside the review\ndtaifm review examples/smart_rules/constraints.yaml proposed.yaml \\\n  --state examples/smart_rules/state.json --bundle review.json\n\n# Replay a bundle and verify it reproduces exactly (exit 1 on mismatch)\ndtaifm replay review.json\n\n# Read-only summary of a bundle — no execution\ndtaifm inspect review.json\n\n# Build a deterministic feedback artifact from validation failures (NO execution)\ndtaifm feedback constraints.yaml rules.yaml --out feedback.json\n\n# Hand the deterministic feedback to a teacher and write a revised rule file\n# (the revised file is NOT validated or executed by repropose — only review/validate does that)\ndtaifm repropose constraints.yaml rules.yaml --teacher mock --out revised.yaml\n```\n\n`--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.\n\n## Audit Bundles \u0026 Replay\n\n`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.\n\n`dtaifm replay review.json` re-executes the review on a fresh checkout and verifies:\n\n1. Embedded inputs match their recorded hashes (catches input tampering).\n2. Stored validation/execution results match their recorded hashes (catches result tampering).\n3. Recomputed validation/execution from inputs match the stored hashes (catches framework non-determinism or domain semantic drift).\n\nA 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.\n\n### Public Python API\n\n```python\nfrom dtaifm import review, replay, inspect_bundle\n\nbundle = review(\n    constraints_path=\"constraints.yaml\",\n    rules_path=\"rules.yaml\",\n    state_path=\"state.json\",\n    domain_id=\"smart_home\",\n    bundle_path=\"review.json\",  # optional\n)\n\nresult = replay(\"review.json\")          # or replay(bundle)\nassert result.success\nassert result.inputs_intact\nassert result.validation_matches\n\nsummary = inspect_bundle(\"review.json\")  # pure read; no execution\n```\n\nThe CLI is a thin wrapper over these three functions.\n\n### Pipeline\n\n```\npropose   -\u003e   validate   -\u003e   run      (or `review` for all three in one report)\n[teacher]      [student]       [runtime]\nartifact       gate            execution\n```\n\n`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.\n\n### File formats (all versioned)\n\nEvery dtaifm file carries `schema_version: \"0.1\"` at the top level. Loaders reject files with a missing or unsupported version.\n\n**constraints.yaml**\n\n```yaml\nschema_version: \"0.1\"\nconstraints:\n  - id: no_auto_unlock\n    description: \"Never unlock doors automatically.\"\n    type: absolute_prohibition\n    applies_to: [front_door]\n    action: unlock\n```\n\n**rules.yaml** (proposed rules carry provenance; hand-written rules may omit it)\n\n```yaml\nschema_version: \"0.1\"\nrules:\n  - id: r_x\n    name: \"Example\"\n    trigger: { device: motion_sensor, event: motion_detected }\n    conditions: []\n    actions:\n      - { device: hallway_light, action: turn_on }\n    satisfies_constraints: [motion_light_hours]\n    explanation: \"What the rule does.\"\n    rationale: \"Why the teacher proposed it.\"\n    proposed_by: mock\n    proposal_id: 7f3c...\n    created_at: \"2026-05-24T10:00:00+00:00\"\n```\n\n**state.json**\n\n```json\n{\n  \"schema_version\": \"0.1\",\n  \"event\":   { \"device\": \"motion_sensor\", \"type\": \"motion_detected\" },\n  \"time\":    \"2024-01-01T23:00:00\",\n  \"mode\":    \"normal\",\n  \"devices\": { \"ac\": \"off\", \"heating\": \"off\" }\n}\n```\n\n### Execution trace\n\nEvery `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.\n\n```\nFIRED    [r_motion_night_light]  trigger matched and all conditions passed\n             - time_range {'start_hour': 22, 'end_hour': 6} -\u003e ok\n             - mode_not {'mode': 'security'} -\u003e ok\nSKIPPED  [r_heating_cold]  trigger did not match (rule expects thermostat.temperature_below_threshold)\n```\n\n### Expected demo output\n\n```\n=== dtaifm Smart Home Demo ===\nAI proposes. The deterministic layer disposes.\n\n──────────────────────────────────────────────────\n  1. Constraints (defined by humans)\n──────────────────────────────────────────────────\n  [no_auto_unlock]      Never unlock doors automatically.\n  [no_hvac_conflict]    Do not turn heating and AC on at the same time.\n  [motion_light_hours]  Lights may turn on from motion only during configured night hours.\n  [security_override]   Security mode overrides comfort automation.\n  [rule_must_explain]   Every generated rule must explain which constraint it satisfies.\n\n──────────────────────────────────────────────────\n  2. Teacher proposes rules (AI / mock)\n──────────────────────────────────────────────────\n  3 rule(s) proposed:\n  [r_motion_night_light]  Motion-Activated Night Light\n  [r_auto_unlock_door]    Auto-Unlock on Arrival (UNSAFE)\n  [r_heating_cold]        Activate Heating When Cold\n\n──────────────────────────────────────────────────\n  3. Validator reviews each rule (deterministic)\n──────────────────────────────────────────────────\n  APPROVED  [r_motion_night_light]  Motion-Activated Night Light\n  REJECTED  [r_auto_unlock_door]    Auto-Unlock on Arrival (UNSAFE)\n            ! [no_auto_unlock]    Rule 'r_auto_unlock_door' performs prohibited action 'unlock' on device 'front_door'.\n            ! [rule_must_explain] Rule 'r_auto_unlock_door' does not declare which constraints it satisfies.\n  APPROVED  [r_heating_cold]        Activate Heating When Cold\n\n──────────────────────────────────────────────────\n  4. Runtime executes approved rules (deterministic)\n──────────────────────────────────────────────────\n\n  Event: motion_detected at 23:00 — normal mode\n    -\u003e [r_motion_night_light] hallway_light: turn_on  {'duration': 300}\n\n  Event: motion_detected at 23:00 — security mode\n    -\u003e (no rules triggered)\n\n  Event: motion_detected at 14:00 — normal mode (outside night hours)\n    -\u003e (no rules triggered)\n\n  Event: temperature_below_threshold — AC off, normal mode\n    -\u003e [r_heating_cold] heating: turn_on\n```\n\n## Core Concepts\n\n| Concept | Role |\n|---|---|\n| `Constraint` | A hard rule a system must never violate. Defined by humans in YAML. Immutable at runtime. |\n| `Rule` | A candidate action proposed by the AI teacher. Contains trigger, conditions, actions, and a declaration of which constraints it satisfies. |\n| `RuleSet` | A collection of proposed rules returned by a single teacher call. |\n| `ValidationResult` | The outcome of checking one Rule against all Constraints. Carries violations with reasons. |\n| `ExecutionResult` | The outcome of firing approved rules against a live system state event. |\n\n### Constraint types\n\n| Type | Description |\n|---|---|\n| `absolute_prohibition` | A specific action on a specific device is never allowed. |\n| `mutual_exclusion` | Two or more devices must never be activated simultaneously. |\n| `temporal_restriction` | A device may only be controlled via a trigger within a time window. |\n| `mode_override` | A named mode (e.g. `security`) supersedes comfort automation. |\n| `metadata_requirement` | Every rule must carry specified metadata fields. |\n\n## Defining Your Own Constraints\n\n```yaml\n# constraints.yaml\nconstraints:\n  - id: no_auto_unlock\n    description: \"Never unlock doors automatically.\"\n    type: absolute_prohibition\n    applies_to:\n      - front_door\n    action: unlock\n```\n\nLoad them in Python:\n\n```python\nimport yaml\nfrom dtaifm.core.constraint import Constraint\n\nwith open(\"constraints.yaml\") as f:\n    data = yaml.safe_load(f)\nconstraints = [Constraint.from_dict(c) for c in data[\"constraints\"]]\n```\n\n## Domains\n\ndtaifm 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.\n\nTwo domains ship in the box:\n\n| Domain id | What it covers |\n|---|---|\n| `smart_home` (default) | Residential automation: lights, HVAC, locks, sensors. |\n| `network_automation` | Router/switch config, BGP, maintenance windows; includes custom evaluators for `companion_action_required`, `action_target_limit`, `mode_required`. |\n\nEvery CLI command takes `--domain`:\n\n```bash\n# smart_home (default)\ndtaifm validate examples/smart_rules/constraints.yaml examples/smart_rules/rules.yaml\n\n# network_automation\ndtaifm validate --domain network_automation \\\n  examples/network_automation/constraints.yaml examples/network_automation/rules.yaml\n\ndtaifm review --domain network_automation \\\n  examples/network_automation/constraints.yaml examples/network_automation/rules.yaml \\\n  --state examples/network_automation/state.json\n```\n\n### Adding your own domain\n\n```python\nfrom dtaifm.domains.base import Domain\nfrom dtaifm.domains.registry import register_domain\n\nMY_DOMAIN = Domain(\n    id=\"my_domain\",\n    version=\"0.1\",\n    trigger_events=frozenset({\"order_placed\", \"shipment_delayed\"}),\n    condition_types=frozenset({\"time_range\", \"mode_not\", \"device_state\"}),\n    action_kinds=frozenset({\"notify\", \"refund\", \"escalate\"}),\n    extra_constraint_evaluators={\n        # \"my_custom_type\": my_evaluator,  # (rule, constraint) -\u003e ConstraintViolation | None\n    },\n)\nregister_domain(MY_DOMAIN)\n# Now: dtaifm validate --domain my_domain ...\n```\n\nProvider 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.\n\n## Teachers\n\nA **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.**\n\n### Mock teacher (always available, no install needed)\n\n```bash\ndtaifm propose constraints.yaml --teacher mock --out proposed.yaml\n```\n\n### Anthropic Claude adapter (optional extra)\n\n```bash\npip install 'dtaifm[anthropic]'\nexport ANTHROPIC_API_KEY=sk-ant-...\n\n# (optional) override the default model:\nexport ANTHROPIC_MODEL=claude-opus-4-7\n\ndtaifm propose constraints.yaml --teacher anthropic --domain smart_home --out proposed.yaml\n```\n\nThe 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.\n\n### OpenAI adapter (optional extra)\n\n```bash\npip install 'dtaifm[openai]'\nexport OPENAI_API_KEY=sk-...\n\n# (optional) override the default model:\nexport OPENAI_MODEL=gpt-5.4\n\ndtaifm propose constraints.yaml --teacher openai --domain smart_home --out proposed.yaml\n```\n\nThe adapter calls OpenAI's **Responses API** and requests **Structured Outputs** (a `json_schema` `text.format`) so the model returns the rule envelope directly; the response text is then routed through the **same strict parser** as every other adapter. Default model: `gpt-5.5` (override with `--model` or `OPENAI_MODEL`). Like the Anthropic adapter, the OpenAI SDK is **not** a core dependency — `pip install dtaifm` still works without it, and `--teacher openai` without the extra installed fails with a clear install hint.\n\n\u003e The schema is sent in **non-strict** mode on purpose. OpenAI's strict Structured Outputs require `additionalProperties: false` on every object, which would forbid dtaifm's open-ended action `parameters` and per-type condition fields. The non-strict schema steers structure while the deterministic parser stays the authoritative gate — the adapter only translates.\n\n### Local teachers — Ollama and Lemonade (no API keys)\n\nBoth adapters speak plain JSON over HTTP via stdlib — no extra install required. Defaults:\n\n| Teacher | Default base URL | Endpoint | Env var |\n|---|---|---|---|\n| `ollama` | `http://localhost:11434` | `POST /api/chat` | `DTAIFM_OLLAMA_BASE_URL` |\n| `lemonade` | `http://localhost:13305` | `POST /v1/chat/completions` (OpenAI-compatible) | `DTAIFM_LEMONADE_BASE_URL` |\n\nOverride precedence is **CLI flag \u003e env var \u003e default**, and trailing slashes are normalized.\n\n```bash\n# Local Ollama (default endpoint, llama3.2 by default)\ndtaifm propose constraints.yaml --teacher ollama --domain smart_home --out proposed.yaml\n\n# Local Ollama with a specific model\ndtaifm propose constraints.yaml --teacher ollama --model qwen3:0.6b --out proposed.yaml\n\n# Lemonade on a remote workstation\ndtaifm propose constraints.yaml \\\n  --teacher lemonade \\\n  --teacher-base-url http://192.0.2.10:13305 \\\n  --model Qwen3-0.6B-GGUF \\\n  --out proposed.yaml\n\n# Or, via env var:\nexport DTAIFM_LEMONADE_BASE_URL=http://192.0.2.10:13305\ndtaifm propose constraints.yaml --teacher lemonade --model Qwen3-0.6B-GGUF --out proposed.yaml\n```\n\nThe 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.\n\n### Diagnosing your local setup\n\n```bash\ndtaifm teachers           # list registered teachers + base URLs + env-var hints\ndtaifm teachers --check   # additionally ping local endpoints; offline servers are reported gracefully\ndtaifm teachers --json    # machine-readable\n```\n\n## Reproposal Loop\n\nTeachers 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.\n\n```bash\n# 1. The teacher's first attempt\ndtaifm propose constraints.yaml --teacher ollama --out v1.yaml\n\n# 2. Inspect what failed (validator only — no execution)\ndtaifm feedback constraints.yaml v1.yaml --out feedback.json\n# feedback.json contains: schema_version, domain, rejected_rules\n#   [{rule_id, name, violations, allowed_triggers, allowed_conditions, allowed_actions}]\n\n# 3. Repropose: the teacher receives the previous rules + the named violations\ndtaifm repropose constraints.yaml v1.yaml --teacher ollama --out v2.yaml\n\n# 4. Now run a real review on the revised file (THIS is where execution happens)\ndtaifm review constraints.yaml v2.yaml --state state.json --bundle review.json\n```\n\n**Guarantees enforced in code:**\n\n- `dtaifm feedback` never instantiates the runtime (tests assert this).\n- `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.\n- 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.\n\nThe principle: **the deterministic layer may teach the teacher, but it never lets the teacher grade itself.**\n\n\u003e **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.\n\n### Inspecting the prompt\n\n```bash\ndtaifm prompt constraints.yaml --teacher anthropic --domain smart_home\n```\n\n`dtaifm prompt` shows the exact text input the adapter would send. It requires no API key and makes no network calls.\n\n### Building your own teacher\n\n```python\nfrom dtaifm.teacher.base import Teacher\nfrom dtaifm.teacher.contract import TeacherRequest, TeacherResponse\nfrom dtaifm.teacher.parser import parse_provider_payload\nfrom dtaifm.teacher.registry import register_teacher\n\nclass CustomTeacher(Teacher):\n    def propose(self, request: TeacherRequest) -\u003e TeacherResponse:\n        # 1. self.render_prompt(request) gives you the standard prompt\n        # 2. Call your provider, request JSON output that matches dtaifm.schema.RULES_SCHEMA\n        # 3. Run the response through parse_provider_payload for strict validation\n        payload = ...  # dict from your provider\n        ruleset = parse_provider_payload(payload, source=\"custom\")\n        return TeacherResponse(ruleset=ruleset, raw_provider_output=...)\n\nregister_teacher(\"custom\", CustomTeacher)\n# Now: dtaifm propose constraints.yaml --teacher custom --out proposed.yaml\n```\n\n`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.\n\n## Developer commands\n\n```bash\n# Install with dev tools (pytest, jsonschema, ruff, build)\npip install -e \".[dev]\"\n\n# Run the test suite (324 tests, fully offline, no API keys)\npytest\n\n# Lint and format\nruff check dtaifm tests\nruff format dtaifm tests\n\n# Build a wheel\npython -m build --wheel\n\n# Smoke-test the wheel in a clean venv (also runs in CI)\npython -m venv /tmp/wheel-test\n/tmp/wheel-test/bin/pip install dist/dtaifm-0.1.3-py3-none-any.whl\n/tmp/wheel-test/bin/dtaifm --help\n```\n\nOptional type checking (`mypy dtaifm`) is supported but not enforced in CI.\n\n## Roadmap\n\n- [x] CLI (`validate`, `run`) with text + JSON output\n- [x] Portable rule files (YAML / JSON)\n- [x] Deterministic execution trace\n- [x] CI on Python 3.11–3.13 (core install + anthropic-extra install)\n- [x] Schema versioning + published JSON Schemas (`dtaifm schema`)\n- [x] `dtaifm propose` (teacher artifact) and `dtaifm review` (combined audit)\n- [x] Rule provenance fields (`proposed_by`, `proposal_id`, `created_at`, `rationale`)\n- [x] Teacher registry (adapter slot — no provider deps in core)\n- [x] `TeacherRequest` / `TeacherResponse` / `PromptContext` provider-neutral contract\n- [x] `dtaifm prompt` — inspect adapter input with no API key\n- [x] Strict provider response parsing (`ProviderResponseError`)\n- [x] Anthropic Claude teacher adapter (optional extra)\n- [x] Domain pack abstraction with registry (`smart_home`, `network_automation`)\n- [x] Domain-aware validator (rejects out-of-vocabulary triggers/conditions/actions)\n- [x] Runtime defense-in-depth: refuses actions outside the active domain\n- [x] Domain-aware prompts (every adapter receives the domain's vocabulary)\n- [x] Replayable audit bundles (`.dtaifm-review.json`) with canonical-JSON SHA-256 hashes\n- [x] `dtaifm replay` and `dtaifm inspect` commands; public Python API (`review`, `replay`, `inspect_bundle`)\n- [x] Tamper detection across inputs, stored results, and recomputed results\n- [x] Local teacher adapters: `ollama` and `lemonade` (no API keys, no extra deps)\n- [x] `dtaifm teachers` / `dtaifm teachers --check` connectivity diagnostics\n- [x] Deterministic feedback artifacts (`dtaifm feedback`) — validation-only, no execution\n- [x] Reproposal loop (`dtaifm repropose`) — teachers consume named violations through the same `TeacherRequest` contract; the revision is written but not validated/executed\n- [x] OpenAI teacher adapter (optional extra) — Responses API + Structured Outputs\n- [ ] Persistent audit log of every propose → validate → execute cycle\n- [ ] Telecom / network automation example\n- [ ] Rust/WASM deterministic runtime\n\n## Contributing\n\nContributions welcome. The most valuable areas right now are teacher adapters for real AI providers and additional constraint types. Open an issue before large PRs.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdtaifm%2Fdtaifm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdtaifm%2Fdtaifm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdtaifm%2Fdtaifm/lists"}