zirenx/digital-twin-composition
Digital Twin Composition Datasets for retrieving and filling DTDL digital-twin interfaces from natural-language requests. All parts live in this one repo as separate configs. Two families The configs come in two provenances that share the same schemas but must not be mixed: synthetic (triplets, interfaces, fill_eval, topics, eval_small, eval_mid) — LLM-generated interfaces and everything derived from them. This is the training pool. real (interfaces_real… See the full description on the dataset page: https://huggingface.co/datasets/zirenx/digital-twin-composition.
Digital Twin Composition
Datasets for retrieving and filling DTDL digital-twin interfaces from natural-language requests. All parts live in this one repo as separate configs.
Two families
The configs come in two provenances that share the same schemas but must not be mixed:
- synthetic (
triplets,interfaces,fill_eval,topics,eval_small,eval_mid) — LLM-generated interfaces and everything derived from them. This is the training pool. - real (
interfaces_real,triplets_real,fill_eval_real) — the same three schemas built from real vendor device interfaces. This is the evaluation pool.
The two catalogues share no interface ids. The retrieval model is trained on triplets and scored on triplets_real, so concatenating a *_real config with its synthetic namesake would leak the evaluation set into training.
Configs
from datasets import load_dataset
triplets = load_dataset("zirenx/digital-twin-composition", "triplets", split="train") # synthetic, train
interfaces = load_dataset("zirenx/digital-twin-composition", "interfaces", split="train")
eval_triplets = load_dataset("zirenx/digital-twin-composition", "triplets_real", split="test") # real, evaluationNested fields are JSON strings
positive, negative, contents, answer and interface hold JSON text, because DTDL payloads are not uniformly typed — contents mixes objects with bare strings, a schema may be a string or an object, and every fill_eval answer has its own key set. Storing them as strings keeps the data lossless; parse a column to get the original value:
import json
interface = json.loads(triplets[0]["positive"])
print(interface["@id"]) # dtmi:<topic>:<Name>;1How the parts join
Every interface id has the form dtmi:<topic>:<Name>;1, and the topic columns are precomputed from the id for convenience. Within the synthetic family <topic> is normally a topics.id; within the real family it is the vendor name (Advantech, ASUS, …), which is deliberately absent from topics — that config describes the synthetic pool only.
fill_eval.interface_id and the id in a parsed triplets.positive both resolve to interfaces.id. The same holds inside the real family against interfaces_real, never across families.
Within each family the three configs are aligned one-to-one: same row count, and the same multiset of interface ids. Every positive and negative resolves to a row in that family's interface catalogue.
Provenance
The synthetic interfaces and topics are LLM-generated by the scripts in the digital-twin-composition project; triplet negatives are sampled from a different topic and verified by an LLM judge plus a lexical near-duplicate filter.
The real configs come from Azure/iot-plugandplay-models, Microsoft's public device-model index. That catalogue was filtered down to the DTDL models that are actually usable here — they parse, and they declare properties worth retrieving and filling — and the same triplet and fill-eval generators were then run over the survivors. Interface ids keep their upstream dtmi:<vendor>:<Model>;1 form, which is why the topic column of the real family holds a vendor name.
