dyon 0.7.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 (121) hide show
  1. dyon-0.7.0/PKG-INFO +419 -0
  2. dyon-0.7.0/README.md +371 -0
  3. dyon-0.7.0/dyon/__init__.py +16 -0
  4. dyon-0.7.0/dyon/_compat.py +66 -0
  5. dyon-0.7.0/dyon/autonomous/__init__.py +19 -0
  6. dyon-0.7.0/dyon/autonomous/base.py +29 -0
  7. dyon-0.7.0/dyon/autonomous/deployer.py +91 -0
  8. dyon-0.7.0/dyon/autonomous/gym_env.py +124 -0
  9. dyon-0.7.0/dyon/autonomous/ooda.py +253 -0
  10. dyon-0.7.0/dyon/autonomous/overseer.py +310 -0
  11. dyon-0.7.0/dyon/autonomous/planner.py +80 -0
  12. dyon-0.7.0/dyon/autonomous/trainer.py +67 -0
  13. dyon-0.7.0/dyon/cli/__init__.py +1 -0
  14. dyon-0.7.0/dyon/cli/main.py +285 -0
  15. dyon-0.7.0/dyon/collection/__init__.py +15 -0
  16. dyon-0.7.0/dyon/collection/aggregate.py +132 -0
  17. dyon-0.7.0/dyon/collection/base.py +87 -0
  18. dyon-0.7.0/dyon/collection/collection_twin.py +106 -0
  19. dyon-0.7.0/dyon/collection/composite.py +194 -0
  20. dyon-0.7.0/dyon/collection/network_twin.py +209 -0
  21. dyon-0.7.0/dyon/collection/orchestrator.py +56 -0
  22. dyon-0.7.0/dyon/connector/__init__.py +12 -0
  23. dyon-0.7.0/dyon/connector/api_connector.py +62 -0
  24. dyon-0.7.0/dyon/connector/base.py +71 -0
  25. dyon-0.7.0/dyon/connector/ditto_connector.py +109 -0
  26. dyon-0.7.0/dyon/connector/mqtt_connector.py +107 -0
  27. dyon-0.7.0/dyon/core/__init__.py +17 -0
  28. dyon-0.7.0/dyon/core/base.py +132 -0
  29. dyon-0.7.0/dyon/core/config.py +157 -0
  30. dyon-0.7.0/dyon/core/events.py +85 -0
  31. dyon-0.7.0/dyon/core/lifecycle.py +85 -0
  32. dyon-0.7.0/dyon/core/registry.py +55 -0
  33. dyon-0.7.0/dyon/core/types.py +57 -0
  34. dyon-0.7.0/dyon/data/__init__.py +13 -0
  35. dyon-0.7.0/dyon/data/management/__init__.py +4 -0
  36. dyon-0.7.0/dyon/data/management/health.py +47 -0
  37. dyon-0.7.0/dyon/data/management/pipeline.py +107 -0
  38. dyon-0.7.0/dyon/data/storage/__init__.py +16 -0
  39. dyon-0.7.0/dyon/data/storage/base.py +119 -0
  40. dyon-0.7.0/dyon/data/storage/influx.py +227 -0
  41. dyon-0.7.0/dyon/data/storage/minio_store.py +84 -0
  42. dyon-0.7.0/dyon/data/storage/mongo.py +117 -0
  43. dyon-0.7.0/dyon/data/storage/postgres.py +51 -0
  44. dyon-0.7.0/dyon/data/storage/provenance.py +124 -0
  45. dyon-0.7.0/dyon/data/storage/redis_store.py +88 -0
  46. dyon-0.7.0/dyon/data/text_ingestor.py +157 -0
  47. dyon-0.7.0/dyon/data/writer.py +103 -0
  48. dyon-0.7.0/dyon/infra/__init__.py +4 -0
  49. dyon-0.7.0/dyon/infra/docker.py +349 -0
  50. dyon-0.7.0/dyon/infra/health_check.py +100 -0
  51. dyon-0.7.0/dyon/intelligent/__init__.py +15 -0
  52. dyon-0.7.0/dyon/intelligent/agent.py +152 -0
  53. dyon-0.7.0/dyon/intelligent/base.py +42 -0
  54. dyon-0.7.0/dyon/intelligent/knowledge_graph.py +220 -0
  55. dyon-0.7.0/dyon/intelligent/mas.py +228 -0
  56. dyon-0.7.0/dyon/intelligent/tools.py +100 -0
  57. dyon-0.7.0/dyon/learning/__init__.py +64 -0
  58. dyon-0.7.0/dyon/learning/_util.py +62 -0
  59. dyon-0.7.0/dyon/learning/demonstrations.py +205 -0
  60. dyon-0.7.0/dyon/learning/features.py +182 -0
  61. dyon-0.7.0/dyon/learning/imitation_trainers.py +168 -0
  62. dyon-0.7.0/dyon/learning/irl_trainers.py +174 -0
  63. dyon-0.7.0/dyon/learning/maxent.py +180 -0
  64. dyon-0.7.0/dyon/learning/pipeline.py +296 -0
  65. dyon-0.7.0/dyon/learning/reward.py +126 -0
  66. dyon-0.7.0/dyon/ml/__init__.py +3 -0
  67. dyon-0.7.0/dyon/ml/corpus.py +102 -0
  68. dyon-0.7.0/dyon/network/__init__.py +4 -0
  69. dyon-0.7.0/dyon/network/ingestor.py +102 -0
  70. dyon-0.7.0/dyon/network/transport.py +107 -0
  71. dyon-0.7.0/dyon/notifications/__init__.py +3 -0
  72. dyon-0.7.0/dyon/notifications/notifier.py +104 -0
  73. dyon-0.7.0/dyon/physical/__init__.py +4 -0
  74. dyon-0.7.0/dyon/physical/adapters/__init__.py +1 -0
  75. dyon-0.7.0/dyon/physical/base.py +34 -0
  76. dyon-0.7.0/dyon/physical/simulator.py +141 -0
  77. dyon-0.7.0/dyon/reactive/__init__.py +16 -0
  78. dyon-0.7.0/dyon/reactive/actions.py +50 -0
  79. dyon-0.7.0/dyon/reactive/base.py +31 -0
  80. dyon-0.7.0/dyon/reactive/fsm_engine.py +205 -0
  81. dyon-0.7.0/dyon/reactive/pid.py +95 -0
  82. dyon-0.7.0/dyon/reactive/rule_engine.py +211 -0
  83. dyon-0.7.0/dyon/reactive/rule_repository.py +185 -0
  84. dyon-0.7.0/dyon/services/__init__.py +4 -0
  85. dyon-0.7.0/dyon/services/api/__init__.py +3 -0
  86. dyon-0.7.0/dyon/services/api/app.py +56 -0
  87. dyon-0.7.0/dyon/services/api/routes.py +94 -0
  88. dyon-0.7.0/dyon/services/api/streaming.py +64 -0
  89. dyon-0.7.0/dyon/services/base.py +66 -0
  90. dyon-0.7.0/dyon/services/composite.py +35 -0
  91. dyon-0.7.0/dyon/services/ditto/__init__.py +4 -0
  92. dyon-0.7.0/dyon/services/ditto/client.py +183 -0
  93. dyon-0.7.0/dyon/services/ditto/sync.py +87 -0
  94. dyon-0.7.0/dyon/session/__init__.py +3 -0
  95. dyon-0.7.0/dyon/session/context.py +175 -0
  96. dyon-0.7.0/dyon/simulation/__init__.py +16 -0
  97. dyon-0.7.0/dyon/simulation/base.py +22 -0
  98. dyon-0.7.0/dyon/simulation/discrete_event.py +61 -0
  99. dyon-0.7.0/dyon/simulation/forecaster.py +55 -0
  100. dyon-0.7.0/dyon/simulation/ode_model.py +77 -0
  101. dyon-0.7.0/dyon/simulation/runner.py +110 -0
  102. dyon-0.7.0/dyon/simulation/surrogate.py +79 -0
  103. dyon-0.7.0/dyon.egg-info/PKG-INFO +419 -0
  104. dyon-0.7.0/dyon.egg-info/SOURCES.txt +119 -0
  105. dyon-0.7.0/dyon.egg-info/dependency_links.txt +1 -0
  106. dyon-0.7.0/dyon.egg-info/entry_points.txt +3 -0
  107. dyon-0.7.0/dyon.egg-info/requires.txt +41 -0
  108. dyon-0.7.0/dyon.egg-info/top_level.txt +1 -0
  109. dyon-0.7.0/pyproject.toml +96 -0
  110. dyon-0.7.0/setup.cfg +4 -0
  111. dyon-0.7.0/tests/test_backward_compat.py +43 -0
  112. dyon-0.7.0/tests/test_config_thresholds.py +54 -0
  113. dyon-0.7.0/tests/test_gym_env.py +52 -0
  114. dyon-0.7.0/tests/test_knowledge_graph.py +37 -0
  115. dyon-0.7.0/tests/test_learning.py +203 -0
  116. dyon-0.7.0/tests/test_lifecycle.py +82 -0
  117. dyon-0.7.0/tests/test_mqtt_topic.py +34 -0
  118. dyon-0.7.0/tests/test_ooda_decide.py +60 -0
  119. dyon-0.7.0/tests/test_provenance.py +56 -0
  120. dyon-0.7.0/tests/test_reactive_fsm.py +144 -0
  121. dyon-0.7.0/tests/test_rule_repository.py +71 -0
dyon-0.7.0/PKG-INFO ADDED
@@ -0,0 +1,419 @@
1
+ Metadata-Version: 2.1
2
+ Name: dyon
3
+ Version: 0.7.0
4
+ Summary: Domain-agnostic Python framework for digital twin architectures
5
+ License: MIT
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: paho-mqtt>=2.0
9
+ Requires-Dist: influxdb-client>=1.40
10
+ Requires-Dist: pymongo>=4.6
11
+ Requires-Dist: redis>=5.0
12
+ Requires-Dist: minio>=7.2
13
+ Requires-Dist: asyncpg>=0.29
14
+ Requires-Dist: pydantic>=2.5
15
+ Requires-Dist: pydantic-settings>=2.1
16
+ Requires-Dist: python-dotenv>=1.0
17
+ Requires-Dist: fastapi>=0.109
18
+ Requires-Dist: uvicorn[standard]>=0.27
19
+ Requires-Dist: httpx>=0.26
20
+ Requires-Dist: scipy>=1.12
21
+ Requires-Dist: numpy>=1.26
22
+ Requires-Dist: simpy>=4.1
23
+ Requires-Dist: onnxruntime>=1.17
24
+ Requires-Dist: scikit-learn>=1.4
25
+ Requires-Dist: prophet>=1.1
26
+ Requires-Dist: transitions>=0.9
27
+ Requires-Dist: simple-pid>=2.0
28
+ Requires-Dist: neo4j>=5.17
29
+ Requires-Dist: vaderSentiment>=3.3
30
+ Requires-Dist: langchain>=0.3
31
+ Requires-Dist: langchain-classic>=0.1
32
+ Requires-Dist: langchain-community>=0.3
33
+ Requires-Dist: langchain-openai>=0.2
34
+ Requires-Dist: langchain-anthropic>=0.2
35
+ Requires-Dist: langchain-ollama>=0.2
36
+ Requires-Dist: stable-baselines3<2.3,>=2.2
37
+ Requires-Dist: gymnasium<0.30,>=0.29
38
+ Requires-Dist: imitation<2.0,>=1.0
39
+ Requires-Dist: pyyaml>=6.0
40
+ Requires-Dist: click>=8.1
41
+ Requires-Dist: pyserial>=3.5
42
+ Provides-Extra: dev
43
+ Requires-Dist: pytest>=7.4; extra == "dev"
44
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
45
+ Requires-Dist: pytest-cov>=4.1; extra == "dev"
46
+ Requires-Dist: ruff>=0.2; extra == "dev"
47
+ Requires-Dist: mypy>=1.8; extra == "dev"
48
+
49
+ # Dyon
50
+
51
+ Dyon is a domain-agnostic Python framework for building **digital twins** —
52
+ live software models of a real-world asset that ingest its sensor data, store and
53
+ model it, react to it, reason about it, and act on it. The same framework works
54
+ for a pump, a patch of soil, a plant, an HVAC unit, or a manufacturing line,
55
+ because nothing in the core knows anything about your domain. You declare what
56
+ your asset's sensors look like, write a small amount of asset-specific glue, and
57
+ the framework supplies the rest.
58
+
59
+ It does that by organising every twin into **six layers** stacked from raw data
60
+ at the bottom to autonomous decision-making at the top, plus two cross-cutting
61
+ subsystems: **connectors** that let twins talk to each other, and **collection
62
+ twins** that group many twins into one. Every layer is optional — you assemble
63
+ only the ones your asset needs.
64
+
65
+ ## The six-layer architecture
66
+
67
+ ```
68
+ ┌─────────────────────────────────────────────────────────────┐
69
+ │ AUTONOMOUS LAYER (Layer 6) │
70
+ │ OODA loop · GoalPlanner · AutonomousOverseer (LLM) │
71
+ │ RL policy (SAC/TD3/PPO/A2C) · PolicyDeployer · HumanNotifier│
72
+ ├─────────────────────────────────────────────────────────────┤
73
+ │ INTELLIGENT LAYER (Layer 5) │
74
+ │ MultiAgentSystem · LangChain DiagnosticAgent │
75
+ │ Neo4j KnowledgeGraph · per-agent status & history │
76
+ ├─────────────────────────────────────────────────────────────┤
77
+ │ REACTIVE LAYER (Layer 4) │
78
+ │ ThresholdRuleEngine · MultiStateFSMRuleEngine │
79
+ │ simple-pid PIDController · PostgreSQL RuleRepository │
80
+ ├─────────────────────────────────────────────────────────────┤
81
+ │ SERVICES LAYER (Layer 3) │
82
+ │ Eclipse Ditto sync · FastAPI REST + SSE chat │
83
+ ├─────────────────────────────────────────────────────────────┤
84
+ │ SIMULATION & MODEL LAYER (Layer 2) │
85
+ │ scipy ODE · ONNX/sklearn surrogate · SimPy · Prophet │
86
+ ├─────────────────────────────────────────────────────────────┤
87
+ │ DATA LAYER (Layer 1) │
88
+ │ MQTT ingest · TelemetryRouter · DataManagementPipeline │
89
+ │ InfluxDB · MongoDB · Redis · MinIO │
90
+ │ ProvenanceLog · SessionStore · TextIngestor │
91
+ └─────────────────────────────────────────────────────────────┘
92
+
93
+ Cross-cutting: Connectors (MQTT · Ditto · HTTP API)
94
+ Collection twins (Aggregate · Collection ·
95
+ Composite · Network)
96
+ Feeding Layer 6: Learning toolkit (imitation + inverse RL → policies/rewards)
97
+ ```
98
+
99
+ What each layer does:
100
+
101
+ | Layer | Responsibility | Core building blocks |
102
+ |-------|----------------|----------------------|
103
+ | **1 — Data** | Ingest telemetry off MQTT, route it to the right stores, smooth it, score asset health, keep an audit trail | `MQTTIngestor`, `TelemetryRouter`, `DataManagementPipeline`, the `Influx`/`Mongo`/`Redis`/`MinIO` adapters, `ProvenanceLog`, `SessionStore`, `TextIngestor` |
104
+ | **2 — Simulation** | Predict, forecast and detect drift against a model of the asset | `ODEModel` (scipy), `ONNXSurrogate` / `SKLearnSurrogate`, `SimPyModel`, `ProphetForecaster`, `ModelRunner` |
105
+ | **3 — Services** | Expose the twin: sync canonical state to Eclipse Ditto, serve a REST + SSE API | `DittoSyncService`, the FastAPI app, the SSE chat stream |
106
+ | **4 — Reactive** | Turn readings into actions automatically: thresholds, a state machine, closed-loop control | `ThresholdRuleEngine`, `MultiStateFSMRuleEngine`, `PIDController`, `RuleRepository` |
107
+ | **5 — Intelligent** | Diagnose *why* something is wrong using a knowledge graph and LLM agents | `KnowledgeGraph` (Neo4j), `DiagnosticAgent` (LangChain), `MultiAgentSystem` |
108
+ | **6 — Autonomous** | Decide *what to do next*: observe-orient-decide-act, goal planning, learned RL policies, human escalation | `OODALoop`, `GoalPlanner`, `AutonomousOverseer`, `PolicyTrainer`/`PolicyDeployer`, `HumanNotifier` |
109
+
110
+ A monitoring-only twin needs just the Data layer (plus MQTT ingest). A
111
+ self-managing asset stacks all six. You never edit the framework to choose — you
112
+ simply return the layers you want from one method (`build_layers`).
113
+
114
+ ## Collection twins
115
+
116
+ Real systems are rarely a single asset. Four collection patterns let you treat a
117
+ group of twins as one twin, each suited to a different topology:
118
+
119
+ | Pattern | Use it for |
120
+ |----------------|-------------------------------------------------------------------|
121
+ | `AggregateDT` | A fleet of *identical* assets — fused state and shared control |
122
+ | `CollectionDT` | Batch monitoring, outlier detection, statistical comparison |
123
+ | `CompositeDT` | Hierarchical systems that exchange boundary conditions, with swap |
124
+ | `NetworkDT` | Graph-topology systems — cascade risk and bottleneck detection |
125
+
126
+ ## Connectors
127
+
128
+ Connectors are the cross-twin transport. A twin can publish to and subscribe
129
+ from another twin over `MQTTConnector`, `DittoConnector`, or `APIConnector`
130
+ (HTTP), all managed through a `ConnectorRegistry`. This is what makes
131
+ *cross-domain* twins possible: a soil twin can feed a plant twin, which can feed
132
+ a yield-forecasting twin.
133
+
134
+ ## Installation
135
+
136
+ Dyon requires **Python 3.11+**. The backing services (MQTT broker,
137
+ databases, Ditto, Neo4j) run in Docker, so you also need Docker with the Compose
138
+ plugin.
139
+
140
+ ```bash
141
+ pip install dyon
142
+ ```
143
+
144
+ That installs the framework and the `dyon` command-line tool. Current version:
145
+ **0.4.3**.
146
+
147
+ ### Coming from `dt-forge`?
148
+
149
+ This framework was previously published as **`dt-forge`** (package `dt_forge`,
150
+ command `dtforge`). It is now **Dyon**, and existing projects keep working with no
151
+ changes: `import dt_forge` and `from dt_forge.… import …` transparently resolve to
152
+ `dyon`, and the `dtforge` command still runs. You will see a one-time
153
+ `DeprecationWarning` pointing you at the new name. To migrate, replace `dt_forge`
154
+ with `dyon` in your imports and `dtforge` with `dyon` on the command line. The
155
+ compatibility shim will be removed in a future major release.
156
+
157
+ ## Quick start
158
+
159
+ ### 1. Configure
160
+
161
+ All configuration comes from environment variables with the `DT_` prefix.
162
+ Nested fields use a **double-underscore** delimiter — `DT_MQTT__BROKER`, not
163
+ `DT_MQTT_BROKER` (the single-underscore form is silently ignored). Put them in a
164
+ `.env` file in your project directory:
165
+
166
+ ```bash
167
+ DT_ASSET_ID=pump_001
168
+ DT_ASSET_TYPE=centrifugal_pump
169
+ DT_ASSET_NAME="Plant A Pump"
170
+
171
+ DT_MQTT__BROKER=localhost
172
+ DT_MQTT__PORT=1883
173
+
174
+ DT_INFLUX__URL=http://localhost:8086
175
+ DT_INFLUX__TOKEN=my-super-secret-token
176
+ DT_INFLUX__ORG=digital_twin
177
+ DT_INFLUX__BUCKET=asset_telemetry
178
+
179
+ DT_MONGO__URI=mongodb://admin:password@localhost:27017
180
+ DT_REDIS__URL=redis://localhost:6379
181
+ DT_DITTO__URL=http://localhost:8080
182
+
183
+ # Optional — only for the intelligent layer
184
+ DT_LLM__PROVIDER=anthropic # openai | anthropic | ollama
185
+ DT_LLM__MODEL=claude-sonnet-4-6
186
+ DT_LLM__API_KEY=sk-ant-...
187
+ DT_NEO4J__URI=bolt://localhost:7687
188
+ ```
189
+
190
+ Every field has a sensible default (see `dyon/core/config.py`), so you only
191
+ set what differs from the defaults.
192
+
193
+ ### 2. Start the infrastructure you need
194
+
195
+ `dyon infra up` writes a `docker-compose.yml` containing exactly the services
196
+ your chosen layers require, then runs `docker compose up -d`:
197
+
198
+ ```bash
199
+ # Minimal monitoring twin (MQTT broker + the four data stores):
200
+ dyon infra up --layers data,network
201
+
202
+ # Add Eclipse Ditto (services) and the Neo4j knowledge graph (intelligent):
203
+ dyon infra up --layers data,network,services,intelligent
204
+
205
+ # Write the compose file without starting anything:
206
+ dyon infra up --layers data,network,services --generate-only
207
+ docker compose up -d
208
+ ```
209
+
210
+ Each layer maps to a fixed set of containers:
211
+
212
+ | Service | Host port(s) | Provisioned for the layer(s) | Purpose |
213
+ |----------------|--------------|------------------------------|----------------------------------|
214
+ | Mosquitto | 1883, 9001 | `network` | MQTT broker (+ WebSocket) |
215
+ | InfluxDB | 8086 | `data` or `network` | Time-series telemetry |
216
+ | MongoDB | 27017 | `data` or `network` | Events + provenance |
217
+ | Redis | 6379 | `data` or `network` | Cache, FSM state, sessions |
218
+ | MinIO | 9000 | `data` or `network` | Trained models, large objects |
219
+ | Eclipse Ditto | 8080 | `services` | Canonical twin state (+ nginx) |
220
+ | Neo4j | 7474, 7687 | `intelligent` | Knowledge graph |
221
+ | Grafana | 3000 | always | Dashboards / observability |
222
+
223
+ > **PostgreSQL is not provisioned by `infra up`.** The optional
224
+ > `RuleRepository` (versioned, hot-reloadable reactive rules) talks to Postgres
225
+ > through `asyncpg`; if you use it, point it at a Postgres instance you run
226
+ > yourself. Everything else in the reactive layer works without it.
227
+
228
+ The command also records your chosen layers in a `.dyon-layers` file. Once the
229
+ containers are up, verify the twin can reach each one:
230
+
231
+ ```bash
232
+ dyon infra check # reads .dyon-layers automatically
233
+ ```
234
+
235
+ Every reachable service prints a `✓`.
236
+
237
+ ### 3. Scaffold a twin
238
+
239
+ ```bash
240
+ dyon init \
241
+ --asset-type centrifugal_pump \
242
+ --name "Plant A Pump" \
243
+ --asset-id pump_001
244
+ ```
245
+
246
+ That writes two files into the output directory:
247
+
248
+ | File | Purpose |
249
+ |-----------|----------------------------------------------------------------|
250
+ | `twin.py` | A runnable twin class — edit `build_layers()` to wire your asset|
251
+ | `.env` | Environment configuration, pre-filled with the defaults |
252
+
253
+ The generated `twin.py` already wires the Data, MQTT-ingest, data-management,
254
+ Ditto-sync and reactive layers, so it runs as soon as you fill in your sensor
255
+ fields. Add the simulation, intelligent and autonomous layers as your twin grows.
256
+
257
+ ### 4. Run
258
+
259
+ ```bash
260
+ python twin.py # run the module directly
261
+ # or, from the project directory:
262
+ dyon run twin # imports twin.py and drives its lifecycle
263
+ ```
264
+
265
+ If you wired the services layer, the FastAPI app serves these endpoints:
266
+
267
+ | Endpoint | Returns |
268
+ |------------------------------|----------------------------------------------------|
269
+ | `GET /health` | Liveness — `{asset_id, status}` |
270
+ | `GET /api/twin/state` | Canonical twin state from Ditto |
271
+ | `GET /api/twin/telemetry` | Latest telemetry feature from Ditto |
272
+ | `GET /api/twin/health-score`| Current health-score feature |
273
+ | `GET /api/twin/events` | Recent events (needs a doc store) |
274
+ | `POST /api/twin/external` | Inbound push from another twin's `APIConnector` |
275
+ | `POST /api/chat` | SSE stream of the LLM diagnostic agent's replies |
276
+
277
+ ## A monitoring twin in ~30 lines
278
+
279
+ ```python
280
+ import asyncio, logging
281
+ from dotenv import load_dotenv
282
+ from dyon.core.config import TwinConfig, SensorFieldSpec
283
+ from dyon.core.base import AbstractDigitalTwin
284
+ from dyon.core.lifecycle import TwinLifecycle
285
+ from dyon.data import InfluxAdapter, MongoAdapter, RedisAdapter
286
+ from dyon.data.writer import TelemetryRouter
287
+ from dyon.data.management import DataManagementPipeline
288
+ from dyon.network import MQTTIngestor
289
+ from dyon.reactive import ThresholdRuleEngine
290
+
291
+ load_dotenv()
292
+ logging.basicConfig(level=logging.INFO)
293
+
294
+ config = TwinConfig(sensor_fields=[
295
+ SensorFieldSpec(name="temperature_c", nominal=25.0, noise_std=0.5,
296
+ warn_threshold=60.0, crit_threshold=75.0),
297
+ SensorFieldSpec(name="pressure_bar", nominal=4.0, noise_std=0.05,
298
+ warn_threshold=3.0, crit_threshold=2.0,
299
+ threshold_direction="low"),
300
+ ])
301
+
302
+ class PumpTwin(AbstractDigitalTwin):
303
+ def build_layers(self):
304
+ ts, doc, cache = InfluxAdapter(self.config), MongoAdapter(self.config), RedisAdapter(self.config)
305
+ router = TelemetryRouter(self.config, self.bus,
306
+ ts_store=ts, doc_store=doc, cache=cache)
307
+ return {
308
+ "data": router,
309
+ "network": MQTTIngestor(self.config, self.bus, router=router),
310
+ "data_mgmt": DataManagementPipeline(self.config, self.bus,
311
+ ts_store=ts, cache=cache),
312
+ "reactive": ThresholdRuleEngine(self.config, self.bus,
313
+ ts_store=ts, cache=cache, doc_store=doc),
314
+ }
315
+
316
+ if __name__ == "__main__":
317
+ lc = TwinLifecycle(); lc.add(PumpTwin(config))
318
+ asyncio.run(lc.run_forever())
319
+ ```
320
+
321
+ Point a real device (or `dyon.physical.simulator.GenericSimulator`) at the
322
+ topic `dt/pump_001/telemetry`, and the twin will route every reading to InfluxDB
323
+ and MongoDB, smooth it, score health, and raise warning/critical events through
324
+ the threshold engine — all automatically. Layer a simulation model, a knowledge
325
+ graph, an LLM agent and an OODA loop on top of this skeleton when you need them.
326
+
327
+ ## Teaching a twin from demonstrations
328
+
329
+ The autonomous layer's RL policies need a reward function. When the behaviour you
330
+ want is a human skill you can't easily write down as a reward, the
331
+ `dyon.learning` toolkit lets you learn it from examples instead:
332
+
333
+ - **Imitation** — clone an expert's actions directly (`BCTrainer`, `DAggerTrainer`).
334
+ - **Inverse RL** — *recover the reward function* behind an expert's behaviour
335
+ (`AIRLTrainer`, `GAILTrainer`, `MaxEntIRLTrainer`).
336
+ - **Plumbing** — `FeatureSpec` (one source of truth for the observation vector),
337
+ `Demonstrations`/`DemonstrationSource`, `LearnedRewardFn` (reuse a recovered
338
+ reward in ordinary RL), and `SkillTransferPipeline` (chain BC → IRL → RL, then
339
+ validate, version and promote).
340
+
341
+ A recovered reward plugs straight back into the same `GenericTwinEnv` and
342
+ `PolicyDeployer` the autonomous layer already uses.
343
+
344
+ ## Extension points
345
+
346
+ The framework is built around protocols and abstract base classes — swap any
347
+ piece without touching the rest:
348
+
349
+ | Extend | By |
350
+ |-------------------------|-------------------------------------------------------|
351
+ | Storage backend | Implementing the `TimeSeriesStore` / `DocumentStore` /|
352
+ | | `CacheStore` / `ObjectStore` protocol |
353
+ | Physics model | Subclassing `ODEModel` or implementing `TwinModel` |
354
+ | ML surrogate | Implementing `TwinModel` over ONNX or scikit-learn |
355
+ | Discrete-event model | Wrapping a generator with `SimPyModel` |
356
+ | Forecaster | Wrapping a time-series model with `ProphetForecaster` |
357
+ | Custom reactive rule | Implementing `Rule.evaluate(readings)` |
358
+ | N-state FSM | Subclassing `MultiStateFSMRuleEngine` |
359
+ | Diagnostic agent tools | Overriding `DiagnosticAgent._build_extra_tools()` |
360
+ | LLM provider | Setting `DT_LLM__PROVIDER` (openai/anthropic/ollama) |
361
+ | Goal-based assessment | Subclassing `GoalPlanner.assess()` |
362
+ | Strategic LLM decisions | Wiring an `AutonomousOverseer` into the `OODALoop` |
363
+ | RL policy | `PolicyTrainer` + `PolicyDeployer` (Stable-Baselines3)|
364
+ | Reward function | Passing `reward_fn` to `GenericTwinEnv`, or learning |
365
+ | | one with `dyon.learning` |
366
+ | Notification channel | Implementing a `NotificationBackend` (email/Slack/web)|
367
+ | Cross-twin transport | Implementing `ConnectorProtocol` |
368
+ | New collection pattern | Subclassing `AbstractCollectionTwin` |
369
+ | Domain session state | Subclassing `SessionContext`, using `SessionStore[T]` |
370
+
371
+ ## Technology stack
372
+
373
+ | Concern | Library |
374
+ |------------------|----------------------------------------------------|
375
+ | Messaging | Eclipse Mosquitto / paho-mqtt 2.x |
376
+ | Time-series | InfluxDB 2 (`influxdb-client`) |
377
+ | Documents | MongoDB (`pymongo` + `motor`) |
378
+ | Cache / pub-sub | Redis |
379
+ | Object storage | MinIO (S3 API) |
380
+ | Relational | PostgreSQL (`asyncpg`) — optional, for `RuleRepository` |
381
+ | Twin state | Eclipse Ditto |
382
+ | Knowledge graph | Neo4j 5 |
383
+ | Web API | FastAPI + Uvicorn + sse-starlette |
384
+ | Config | Pydantic v2 + pydantic-settings |
385
+ | State machine | `transitions` |
386
+ | PID control | `simple-pid` |
387
+ | Physics | SciPy (`solve_ivp`) |
388
+ | Surrogate / ML | ONNX Runtime + scikit-learn |
389
+ | Forecasting | Prophet |
390
+ | Discrete event | SimPy |
391
+ | Sentiment / NLP | vaderSentiment (swappable for any callable) |
392
+ | Agents | LangChain (+ langchain-classic / -community) |
393
+ | LLM providers | OpenAI · Anthropic · Ollama |
394
+ | RL | Stable-Baselines3 + Gymnasium |
395
+ | Learning-from-demo | `imitation` (BC, DAgger, AIRL, GAIL) + a built-in MaxEnt IRL |
396
+ | CLI | Click |
397
+
398
+ ## CLI reference
399
+
400
+ ```bash
401
+ dyon init --asset-type TYPE --name NAME --asset-id ID [--out DIR]
402
+ dyon infra up --layers data,network,services[,intelligent] [--out FILE] [--generate-only]
403
+ dyon infra check [--layers ...] # default: read .dyon-layers
404
+ dyon run [TWIN_MODULE] # default module: "twin"
405
+ dyon train [--algorithm SAC|TD3|PPO|A2C] [--timesteps N]
406
+ [--env-module M] [--save NAME]
407
+ ```
408
+
409
+ Run any command with `--help` for the full flag list.
410
+
411
+ ## Learn more
412
+
413
+ The `guide/` folder in the source repository is the complete developer manual —
414
+ twelve chapters that take you from the mental model to a full worked example,
415
+ layer by layer.
416
+
417
+ ## License
418
+
419
+ MIT