Structured Input Spaces
Most models read every input the same way: one window length, one sample rate, applied to all of them. A structured input space breaks that assumption. The model's flat input vector is carved into named inputs, and each one carries its own window length, sample rate, and interpolation method.
That means a flow reading sampled every second and a temperature sampled every hour can feed the same model, in one inference, without either one being resampled to the other's cadence.
A structured model's input tensor is rank-2 — [1, flat_dim] — where flat_dim is the sum of every input's window length. The per-input windows are declared in the model file (or carved during upload), not derived from the graph. See Model Inference Requirements for how this compares to Flat and Time-series shapes.
When to Use One
There is no benefit to a structured space when every input would end up with the same window and rate. A Flat or Time-series model is simpler to configure and supports Memory Only, which a structured model does not.
Anatomy of a Space
A structured space is a small tree. Containers group inputs; each leaf of the tree is one signal, and one input binding is created per leaf.
Each leaf occupies a contiguous run of slots, and the runs tile the vector exactly — no gaps, no overlap, and the total must equal the model graph's input width.
Declaring a Space
There are two routes. Either the model file already declares its own space, or you carve one during upload.
Route 1: Embedded in the model file
An ONNX file can carry its input space as embedded metadata under the koios.observation_space key, alongside the training and binding metadata described in Managing Model Files. The koios-model-utils library writes it:
The flatten order is the observation space's own resolved child order, used verbatim, so the live vector Koios assembles matches the one the training script fed the model. Building a space from a live gymnasium space needs the library's optional gymnasium extra:
The library supports continuous Box leaves inside Dict and Tuple containers. Discrete, multi-binary, and multi-dimensional spaces are not supported.
A file that carries this metadata is recognized as Structured on upload, and the space is re-applied every time that file is activated.
Route 2: Carve it in the Input Shape step
A plain rank-2 file with no embedded space can be carved by hand. Every upload continues to an Input Shape step after the file is accepted, offering four cards:
Cards that contradict the file are disabled and give the reason on hover. A rank-3 file cannot be read as Structured, and a file that declares its own space cannot be read as anything else — for that file the step shows the space as a read-only tree and you click Continue.
Choosing Structured on a file with no embedded space opens the slice builder.
The Slice Builder
The builder carves the model's flat input vector into named inputs. One row per input:
Use Add input to append a row — its window defaults to whatever is left unassigned. A progress bar and an N / M slots assigned badge track the carve: indigo while slots remain, red if you overshoot, teal when it tiles exactly.
Save shape stays disabled until every row is named and sized and the windows sum to exactly the model's input width. Its tooltip says what is missing: Assign all 18 slots (12 assigned), or Name every input (names must be unique).
The carve is set during upload. To change it later, upload the file again and carve it differently.
How Bindings Map to Inputs
One input binding is created per leaf, in flatten order:
- Binding order is 1-based. Input 1 is the first window in the flat vector, input 2 the next, and so on.
- Binding orders must be unique. Windows are keyed by binding order at assembly, so a duplicate would silently collapse two inputs into one. Duplicates are rejected and the model refuses to run — see Model Configuration Errors for what that looks like and how to renumber.
- Binding orders must be dense. They run 1..N with no gaps, and the count must equal the number of leaves the space declares.
- The binding's name defaults to the last segment of its path —
obs/flowbecomesflow.
Some binding fields belong to the model file and some are yours to edit:
On the Bindings tab
Input cards are grouped under the input they belong to, and each card reports the window in use — for example 12 samples × 1.0 h. Hover that text for the full story: sample spacing, lookback, the slots the input occupies in the flat vector, and its interpolation method.
Open a card and the drawer gains a fourth section, Sampling:
Full detail on the other sections is in Assigning Bindings.
Assembly at Scan Time
On every scan, Koios:
- Fixes one reference instant for the whole model — the right edge every input's window ends at.
- Resamples each input on its own cadence onto its own grid, ending at that instant.
- Normalizes and calibrates each value using that binding's own settings.
- Lays the windows end to end in ascending binding order, producing the rank-2
[1, flat_dim]vector the model was trained on.
Because the windows are concatenated whole rather than interleaved by time step, the result matches a gymnasium flatten of the same observation. Samples within each window run oldest first unless the model file declares that input as newest-first.
Warm-Up and History
A model cannot infer until every input has enough history to fill its window. For a structured model that happens per input, so one binding can be ready while another is still filling — the model waits for all of them.
- On enable, Koios makes a single attempt to backfill each input from long-term history, at that input's own rate and for that input's own window. Where no history exists, it falls back to holding the tag's current live value flat across the window.
- Until a window fills, that binding reports Not Enough Historical Depth. This is normal for a newly bound tag and clears on its own as data accumulates.
- Initialize history seeds each input over its own window rather than one model-wide window. Run it for the whole model from the three-dot menu in the model header, or for a single binding from that binding's configuration drawer.
- Stale-data detection measures the allowed missed samples against that input's own sample rate.
An input with a long window over a slow signal takes correspondingly longer to warm up. An hourly input with a 12-sample window needs 12 hours of history before it is ready, unless backfill or Initialize history can supply it.
Restrictions
Where to See the Space
What's Next
- Model Inference Requirements: the three input shapes and how tensors are built
- Assigning Bindings: per-input sampling controls and every other binding setting
- Managing Model Files: the embedded metadata a structured file carries
- Configuring a Model: why Memory Only and a structured space are mutually exclusive
