Config Adapters¶
Many simulation models store their configuration as a directory of YAML, CSV,
or HOCON files — sometimes dozens of them, with inheritance and overrides spread
across nested folders. Passing that as config={"path": "configs/"} to Consist
would miss changes inside the files, and dumping the entire config tree as a
dict snapshot would produce enormous, hard-to-query records.
Config adapters solve this. They know how a specific model organizes its config files, so they can walk the directory, hash each file individually, extract the parameters that matter for calibration, and register everything as queryable artifacts — without you having to write any of that discovery logic yourself.
The result: cache invalidation that catches any config file change, plus SQL tables of key parameters you can query across runs.
When to use an adapter vs. plain config¶
| Use an adapter | Use config= directly |
|
|---|---|---|
| Config location | Directory of YAML/CSV/HOCON files | Python dict or Pydantic model |
| Config structure | Layered inheritance, file hierarchies | Flat or lightly nested |
| Queryable parameters | Calibration-sensitive values across many files | Simple facets suffice |
| File tracking | Need per-file content hashing and artifact lineage | No file artifacts needed |
For in-memory configuration, see Config, Facets, and Identity Inputs.
What an adapter does¶
When you call tracker.canonicalize_config(adapter=my_adapter), Consist runs
three steps:
- Discover — walks the config directory, locates relevant files, and computes a content hash per file
- Canonicalize — extracts calibration-sensitive parameters, builds artifact specs for each config file, and constructs the identity payload that will contribute to the run signature
- Ingest — logs each config file as a tracked artifact and writes extracted parameters to queryable DuckDB tables
After this, every run backed by that adapter records exactly which config files were used, what their content was, and which parameters changed between runs. When a cache miss occurs, Consist can tell you which specific files or keys changed — not just that "config changed."
Adapter guides¶
Each guide covers discovery behavior, which parameters are extracted, the ingestion schema, and query examples.
GTFS is a little different from the other adapters: it is a standardized feed format, not a bespoke model config tree. If you only need the high-level transit story, start with GTFS Support. If you are wiring GTFS into BEAM, use the BEAM guide as the entry point.
Writing your own adapter¶
Adapters follow a Python Protocol, so you can add support for any model with
file-based configuration. The ActivitySim adapter source
is the best reference: it covers layered YAML discovery, CSV extraction, and
the CanonicalizationResult structure that Consist expects back.
The core contract: your adapter's canonicalize() method must return a
CanonicalizationResult with an identity= payload. Consist uses that manifest
for cache-miss explanations — so misses can report exactly which config
references were added, removed, or changed, rather than falling back to a
generic "config changed" message.
Read canonicalization facts inside a wrapped step¶
When a Python step needs to stage or inspect the files that its configuration
references, request a RunContext. After Consist applies the config plan and
before it calls the step, ctx.canonicalization exposes the immutable facts
from the same canonicalization pass:
from consist import ExecutionOptions, RunContext
def launch_model(ctx: RunContext) -> None:
snapshot = ctx.canonicalization
if snapshot is None: # no adapter was configured for this run
return
for item in snapshot.references:
reference = item.reference
print(reference.config_key, item.resolved_path, item.artifact_keys)
tracker.run(
fn=launch_model,
name="model",
adapter=adapter,
execution_options=ExecutionOptions(inject_context="ctx"),
)
reference.raw_value is the value found in the model configuration and
reference.canonical_value is Consist's portable identity value.
item.resolved_path is only the local path observed during canonicalization.
It is not an execution path: staging, copying, rewriting, and container mount
translation remain downstream responsibilities. Likewise, an empty
item.artifact_keys tuple accurately means Consist did not log an artifact for
that reference; it does not authorize consumers to guess one from a filename.
When an adapter selects one semantically distinct artifact under a broader
reference, item.artifact_members records its role, exact persisted artifact
key, observed local path, and narrow selection metadata. Each member key is
also present in item.artifact_keys. Member paths remain canonicalization-time
observations, not portable identities or final execution paths.
This view is available only to Python callables with injected context. Cache hits do not execute the callable, and native container runs do not receive a Python context or a serialized snapshot.
See Also¶
- Config, Facets, and Identity Inputs — In-memory config, facets, and identity inputs
- Data Storage and Ingestion Strategy — DLT-based data ingestion
- Caching and Artifact Hydration Patterns — Run signature and caching behavior