@zhuoyuezs/ml-platform 0.1.8 → 0.1.10
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.
- package/README.md +7 -3
- package/package.json +1 -1
- package/checksums.json +0 -155
- package/release.json +0 -52
- package/runtime/business-client/README.md +0 -78
- package/runtime/business-client/package-lock.json +0 -19
- package/runtime/business-client/package.json +0 -23
- package/runtime/business-client/src/catalog.js +0 -206
- package/runtime/business-client/src/cli.js +0 -533
- package/runtime/business-client/src/config.js +0 -56
- package/runtime/business-client/src/http.js +0 -254
- package/skills/feature-management/SKILL.md +0 -479
- package/skills/feature-management/agents/openai.yaml +0 -4
- package/skills/feature-management/assets/catalog-template/catalog.json +0 -23
- package/skills/feature-management/assets/catalog-template/datasets/example_temperature_training.v1.json +0 -40
- package/skills/feature-management/assets/catalog-template/feature_sets/example_temperature_core.v1.json +0 -14
- package/skills/feature-management/assets/catalog-template/features/example_temperature_mean_5m.v1.json +0 -28
- package/skills/feature-management/assets/catalog-template/operator_package/pyproject.toml +0 -12
- package/skills/feature-management/assets/catalog-template/operator_package/src/business_feature_operator_template/__init__.py +0 -39
- package/skills/feature-management/assets/catalog-template/operator_package/tests/test_operator.py +0 -83
- package/skills/feature-management/assets/catalog-template/operators/example_temperature_features.v1.json +0 -58
- package/skills/feature-management/assets/catalog-template/parameters/example_temperature.v1.json +0 -58
- package/skills/feature-management/references/commands.md +0 -358
- package/skills/feature-management/references/contracts.md +0 -719
- package/skills/feature-management/references/operator-authoring.md +0 -175
- package/skills/feature-management/references/platform-capability-guide.md +0 -75
- package/skills/feature-management/references/supervised-datasets.md +0 -101
- package/skills/model-lifecycle-management/SKILL.md +0 -38
- package/skills/model-lifecycle-management/agents/openai.yaml +0 -4
- package/skills/model-lifecycle-management/references/discovery.md +0 -89
- package/skills/model-lifecycle-management/references/evaluation.md +0 -172
- package/skills/model-lifecycle-management/references/packaging.md +0 -51
- package/skills/model-lifecycle-management/references/training-contracts.md +0 -139
- package/skills/model-lifecycle-management/references/training.md +0 -81
|
@@ -1,719 +0,0 @@
|
|
|
1
|
-
# Current Contract Reference
|
|
2
|
-
|
|
3
|
-
Use this reference for the implemented V1 schemas. Prefer the running CLI and `src/data_platform_demo/schemas.py` when they disagree with historical design documents.
|
|
4
|
-
|
|
5
|
-
## Contents
|
|
6
|
-
|
|
7
|
-
1. Naming and versioning
|
|
8
|
-
2. Projects and namespacing
|
|
9
|
-
3. Parameter
|
|
10
|
-
4. Operator
|
|
11
|
-
5. Feature
|
|
12
|
-
6. FeatureSet
|
|
13
|
-
7. DatasetManifest
|
|
14
|
-
8. Catalog
|
|
15
|
-
9. Cross-object validation
|
|
16
|
-
|
|
17
|
-
## Naming And Versioning
|
|
18
|
-
|
|
19
|
-
- Use identifiers matching `^[A-Za-z0-9][A-Za-z0-9_-]*$` and at most 128 characters.
|
|
20
|
-
- Prefer lowercase snake_case business identifiers.
|
|
21
|
-
- Use explicit immutable versions such as `v1`, `v2`.
|
|
22
|
-
- Create a new version when any semantic field changes.
|
|
23
|
-
- Keep credentials in the configured profile or server Secret, never in an asset.
|
|
24
|
-
|
|
25
|
-
## Projects And Namespacing
|
|
26
|
-
|
|
27
|
-
- Every registry asset is namespaced by a `project`. Its true registry key is
|
|
28
|
-
`project/name:version`, so `name:version` alone is unique only within one
|
|
29
|
-
project; the same `name:version` may exist independently under other projects.
|
|
30
|
-
- The platform ships a built-in `default` project. When no project is given,
|
|
31
|
-
assets are created and read under `default`.
|
|
32
|
-
- Conceptually a project is a namespace, not an attribute of the asset. In the
|
|
33
|
-
current implementation, however, the project a Parameter/Operator/Feature/
|
|
34
|
-
FeatureSet/DatasetManifest is **created** under comes from the optional
|
|
35
|
-
`project` field in its own JSON spec (it defaults to `default` when omitted).
|
|
36
|
-
So to place an asset in a non-default project, set `"project": "<name>"` in the
|
|
37
|
-
spec before `apply`.
|
|
38
|
-
- For a confirmed non-default target, include that field in every draft
|
|
39
|
-
Parameter, Operator, Feature, Feature input, FeatureSet, and DatasetManifest;
|
|
40
|
-
do not rely on Pydantic's `default` fallback during authoring.
|
|
41
|
-
- Reads and filters, by contrast, take the project out-of-band: `list-* --project`
|
|
42
|
-
and the leading project argument of the artifact commands. `apply --project`
|
|
43
|
-
scopes the comparison snapshot that publication is validated against.
|
|
44
|
-
- All references inside one catalog resolve within a single project. Cross-project
|
|
45
|
-
references (a Feature in project A depending on a Parameter in project B) are
|
|
46
|
-
rejected by the platform.
|
|
47
|
-
- Immutability is per project: publishing `name:v1` into project A never touches
|
|
48
|
-
`name:v1` in project B.
|
|
49
|
-
|
|
50
|
-
## Parameter
|
|
51
|
-
|
|
52
|
-
Required shape:
|
|
53
|
-
|
|
54
|
-
```json
|
|
55
|
-
{
|
|
56
|
-
"schema_version": "ml_data_platform.parameter/v1",
|
|
57
|
-
"name": "hot_blast_pressure",
|
|
58
|
-
"display_name": "Hot blast pressure",
|
|
59
|
-
"version": "v1",
|
|
60
|
-
"data_type": "time_series",
|
|
61
|
-
"unit": "kPa",
|
|
62
|
-
"expected_frequency": "5s",
|
|
63
|
-
"source": {},
|
|
64
|
-
"time_semantics": {
|
|
65
|
-
"event_time_field": "event_time",
|
|
66
|
-
"ingested_at_field": "ingested_at",
|
|
67
|
-
"timezone": "Asia/Shanghai",
|
|
68
|
-
"availability": {
|
|
69
|
-
"strategy": "source_field",
|
|
70
|
-
"field": "ingested_at",
|
|
71
|
-
"accuracy": "exact"
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
"availability_sla": {
|
|
75
|
-
"max_delay": "PT10M"
|
|
76
|
-
},
|
|
77
|
-
"value_field": "value",
|
|
78
|
-
"quality_rules": {
|
|
79
|
-
"valid_range": [0.0, 450.0],
|
|
80
|
-
"allow_missing": true,
|
|
81
|
-
"rules": [
|
|
82
|
-
{
|
|
83
|
-
"id": "finite_source_values",
|
|
84
|
-
"stage": "normalized_source",
|
|
85
|
-
"check": {"type": "finite"},
|
|
86
|
-
"acceptance": {"max_violation_rate": 0.0},
|
|
87
|
-
"enforcement": "fail"
|
|
88
|
-
}
|
|
89
|
-
]
|
|
90
|
-
},
|
|
91
|
-
"owner": "business_owner"
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
Supported source modes are `direct_column`, `sql`, and `derived`. Prefer `direct_column` for ordinary business parameters.
|
|
96
|
-
|
|
97
|
-
For `sql`, `source.sql` must contain one statement without a trailing semicolon,
|
|
98
|
-
and named placeholders must be supplied through `source.params`; `start` and
|
|
99
|
-
`end` are reserved runtime window parameters. `source.parameters` is not a
|
|
100
|
-
supported field.
|
|
101
|
-
|
|
102
|
-
`availability.strategy` 支持 `source_field`、`fixed_delay` 和 `unsupported`。只有一个
|
|
103
|
-
事件时间列时,可以使用经过数据 owner 确认的固定延迟上界,例如
|
|
104
|
-
`{"strategy":"fixed_delay","delay":"PT10M","accuracy":"upper_bound"}`。
|
|
105
|
-
当前严格 `as_of` 只接受 `exact` 或有合同支撑的 `upper_bound`;修改这些语义需要发布
|
|
106
|
-
新的 Parameter 业务版本。
|
|
107
|
-
|
|
108
|
-
An optional `rounding` section states the fixed-point precision the platform
|
|
109
|
-
enforces at the Parameter boundary:
|
|
110
|
-
|
|
111
|
-
```json
|
|
112
|
-
"rounding": {"mode": "half_up", "decimals": 2}
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
Fixing only the decimal count leaves the tie case to whichever engine evaluates
|
|
116
|
-
it: PostgreSQL `ROUND` on `numeric` rounds halves away from zero (5.625 becomes
|
|
117
|
-
5.63) while Python and NumPy round halves to even (5.625 becomes 5.62). A single
|
|
118
|
-
0.01 difference spreads through rolling windows into many derived Features, so
|
|
119
|
-
declare both fields. `mode` is `half_up` or `half_even`.
|
|
120
|
-
|
|
121
|
-
The platform applies the rule with deterministic decimal quantization after the
|
|
122
|
-
adapter produces the standard metric frame and before quality checks, source
|
|
123
|
-
cache publication/replay, and Operator execution. Prefer an unrounded source
|
|
124
|
-
expression. A source that already applies the same rule is accepted because the
|
|
125
|
-
second application is idempotent, but an upstream path that already discarded a
|
|
126
|
-
tie with a different rule cannot be repaired downstream. Do not repeat the same
|
|
127
|
-
Parameter-level rounding in an Operator. Changing the mode, decimals, or stage
|
|
128
|
-
requires a new Parameter version and the affected reverse-dependency closure.
|
|
129
|
-
|
|
130
|
-
PostgreSQL direct-column example:
|
|
131
|
-
|
|
132
|
-
```json
|
|
133
|
-
{
|
|
134
|
-
"adapter": "postgresql_direct",
|
|
135
|
-
"mode": "direct_column",
|
|
136
|
-
"schema": "process_data",
|
|
137
|
-
"table": "sensor_readings",
|
|
138
|
-
"time_column": "event_time",
|
|
139
|
-
"value_column": "hot_blast_pressure",
|
|
140
|
-
"metric_name": "hot_blast_pressure",
|
|
141
|
-
"unit_column": "unit",
|
|
142
|
-
"filters": {"furnace_id": "BF12"}
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
InfluxDB direct-column example:
|
|
147
|
-
|
|
148
|
-
```json
|
|
149
|
-
{
|
|
150
|
-
"adapter": "influxdb_direct",
|
|
151
|
-
"mode": "direct_column",
|
|
152
|
-
"measurement": "blast_furnace",
|
|
153
|
-
"field": "hot_blast_pressure",
|
|
154
|
-
"metric_name": "hot_blast_pressure",
|
|
155
|
-
"tags": {"furnace_id": "BF12"}
|
|
156
|
-
}
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
Do not put DSNs, access keys, tokens, passwords, or connection endpoints containing credentials in `source`.
|
|
160
|
-
|
|
161
|
-
`quality_rules.rules` defines immutable single-Parameter assertions. Supported
|
|
162
|
-
checks are `not_null`, `finite`, `range`, and `max_gap`. `stage` is one of
|
|
163
|
-
`normalized_source`, `post_preprocess`, or `aligned_grid`; execution follows
|
|
164
|
-
that order. `acceptance` controls evidence thresholds, while `enforcement`
|
|
165
|
-
chooses `observe`, `warn`, or `fail`. Keep cross-Parameter assertions out of a
|
|
166
|
-
Parameter version and declare them in DatasetManifest `parameter_relationships`.
|
|
167
|
-
|
|
168
|
-
`quality_rules.missing_detection` controls source-observation gap detection and
|
|
169
|
-
is independent from value validity. It defaults to `disabled`, which is the
|
|
170
|
-
safe behavior for irregular event Parameters such as tapping events or silicon
|
|
171
|
-
samples. Use `fixed_frequency` only when `expected_frequency` is a real fixed
|
|
172
|
-
duration. For irregular Parameters, use an explicit `max_gap` or an
|
|
173
|
-
`interval_multiplier` over a `mean` or `median` observed interval:
|
|
174
|
-
|
|
175
|
-
```json
|
|
176
|
-
"missing_detection": {
|
|
177
|
-
"mode": "interval_multiplier",
|
|
178
|
-
"multiplier": 10,
|
|
179
|
-
"baseline": "median",
|
|
180
|
-
"min_observations": 2
|
|
181
|
-
}
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
`max_gap` accepts positive short durations including second precision (for
|
|
185
|
-
example `30s`, `10min`, or `2h`). Detection runs on the normalized,
|
|
186
|
-
post-preprocess source frame before target-grid interpolation, and its source
|
|
187
|
-
windows are recorded in the build's `missing_windows.json` artifact.
|
|
188
|
-
|
|
189
|
-
## Operator
|
|
190
|
-
|
|
191
|
-
Feature Operator draft shape:
|
|
192
|
-
|
|
193
|
-
```json
|
|
194
|
-
{
|
|
195
|
-
"schema_version": "ml_data_platform.operator/v1",
|
|
196
|
-
"name": "pressure_features",
|
|
197
|
-
"version": "v1",
|
|
198
|
-
"type": "feature",
|
|
199
|
-
"function_hash": "pressure_features.formulas.v1",
|
|
200
|
-
"entrypoint": "pressure_features:compute_features",
|
|
201
|
-
"code_hash": null,
|
|
202
|
-
"package_uri": null,
|
|
203
|
-
"code_artifact": null,
|
|
204
|
-
"input_schema": {
|
|
205
|
-
"parameters": ["hot_blast_pressure:v1"]
|
|
206
|
-
},
|
|
207
|
-
"output_schema": {
|
|
208
|
-
"columns": ["event_time", "pressure_mean_5m"]
|
|
209
|
-
},
|
|
210
|
-
"config_schema": {
|
|
211
|
-
"properties": {
|
|
212
|
-
"window": {"type": "string"}
|
|
213
|
-
},
|
|
214
|
-
"required": ["window"]
|
|
215
|
-
},
|
|
216
|
-
"runtime": {"engine": "python_entrypoint", "network": "none"},
|
|
217
|
-
"resources": {"timeout_seconds": 60},
|
|
218
|
-
"deterministic": true,
|
|
219
|
-
"supports_batch": true,
|
|
220
|
-
"supports_online": false,
|
|
221
|
-
"owner": "business_owner"
|
|
222
|
-
}
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
For a catalog wheel entry, `catalog.json` supplies the package file. Publication calculates and stores `code_hash`, `package_uri`, and `code_artifact`.
|
|
226
|
-
|
|
227
|
-
Treat `pythonpath://` as platform-owned example behavior. Publish formal business Operators as immutable pure-Python wheels.
|
|
228
|
-
|
|
229
|
-
## Feature
|
|
230
|
-
|
|
231
|
-
```json
|
|
232
|
-
{
|
|
233
|
-
"schema_version": "ml_data_platform.feature/v1",
|
|
234
|
-
"name": "pressure_mean_5m",
|
|
235
|
-
"version": "v1",
|
|
236
|
-
"inputs": [
|
|
237
|
-
{"parameter": "hot_blast_pressure", "version": "v1"}
|
|
238
|
-
],
|
|
239
|
-
"operator": "pressure_features",
|
|
240
|
-
"operator_version": "v1",
|
|
241
|
-
"config": {"window": "5min"},
|
|
242
|
-
"output_column": "pressure_mean_5m",
|
|
243
|
-
"output_dtype": "float64",
|
|
244
|
-
"offline_online_supported": false,
|
|
245
|
-
"owner": "business_owner",
|
|
246
|
-
"description": "Mean pressure over the causal five-minute window."
|
|
247
|
-
}
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
Rules:
|
|
251
|
-
|
|
252
|
-
- Define exactly one public output column per Feature.
|
|
253
|
-
- Keep `inputs` nonempty, unique, versioned, and formula-exact.
|
|
254
|
-
- Require the referenced Operator to have `type=feature`.
|
|
255
|
-
- Require `output_column` to appear in the Operator output schema.
|
|
256
|
-
- Use `name` as the final dataset column; use `output_column` as the Operator's physical result column.
|
|
257
|
-
|
|
258
|
-
## FeatureSet
|
|
259
|
-
|
|
260
|
-
```json
|
|
261
|
-
{
|
|
262
|
-
"schema_version": "ml_data_platform.feature_set/v1",
|
|
263
|
-
"name": "pressure_core",
|
|
264
|
-
"version": "v1",
|
|
265
|
-
"features": [
|
|
266
|
-
{"name": "pressure_mean_5m", "version": "v1"}
|
|
267
|
-
],
|
|
268
|
-
"owner": "business_owner",
|
|
269
|
-
"description": "Ordered pressure model inputs."
|
|
270
|
-
}
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
The list must be nonempty, unique, and ordered exactly as consumers expect.
|
|
274
|
-
|
|
275
|
-
## DatasetManifest
|
|
276
|
-
|
|
277
|
-
```json
|
|
278
|
-
{
|
|
279
|
-
"schema_version": "ml_data_platform.dataset_manifest/v1",
|
|
280
|
-
"dataset_id": "pressure_training_20260701_20260702",
|
|
281
|
-
"dataset_version": "v1",
|
|
282
|
-
"mode": "training",
|
|
283
|
-
"read_policy": "snapshot",
|
|
284
|
-
"time_range": {
|
|
285
|
-
"start": "2026-07-01T00:00:00+08:00",
|
|
286
|
-
"end": "2026-07-02T00:00:00+08:00",
|
|
287
|
-
"grid": "10min"
|
|
288
|
-
},
|
|
289
|
-
"prediction": {
|
|
290
|
-
"horizon": "10min"
|
|
291
|
-
},
|
|
292
|
-
"parameters": [],
|
|
293
|
-
"parameter_missing_policies": [
|
|
294
|
-
{
|
|
295
|
-
"parameter": "hot_blast_pressure",
|
|
296
|
-
"version": "v1",
|
|
297
|
-
"project": "default",
|
|
298
|
-
"policy": "report_only"
|
|
299
|
-
}
|
|
300
|
-
],
|
|
301
|
-
"feature_set": {"name": "pressure_core", "version": "v1"},
|
|
302
|
-
"preprocess": [],
|
|
303
|
-
"output": {
|
|
304
|
-
"format": "parquet",
|
|
305
|
-
"include_stats": true,
|
|
306
|
-
"include_lineage": true,
|
|
307
|
-
"engine": "chronon"
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
Current schema rules:
|
|
313
|
-
|
|
314
|
-
- Accept exactly one `feature_set`.
|
|
315
|
-
- Reject old top-level `features` and `recipe` fields.
|
|
316
|
-
- Use a half-open `[start, end)` target grid. `start` must be earlier than
|
|
317
|
-
`end`, the window must span at least one grid step, both bounds must agree on
|
|
318
|
-
timezone awareness, and `grid` must be one of `1min`, `5min`, `10min`,
|
|
319
|
-
`15min`, `20min`, `30min`, `1h`, `2h`, `1d`. Violations are rejected at
|
|
320
|
-
submission with a field-level error on `time_range`; no Job is created.
|
|
321
|
-
- Require `as_of` only with `read_policy=as_of`.
|
|
322
|
-
- Allow `snapshot_id` only with `read_policy=snapshot`.
|
|
323
|
-
- Use `parameters` only to request explicit Parameter output columns.
|
|
324
|
-
- Declare one dataset-wide `prediction` contract for forecast Features. `horizon`
|
|
325
|
-
accepts non-negative whole-minute durations such as `0min`, `10min`, or `2h`.
|
|
326
|
-
The platform always derives `cutoff_times = target_times - horizon` and records
|
|
327
|
-
that derivation in resolved lineage; it is not a user-configurable policy.
|
|
328
|
-
|
|
329
|
-
### Optional sections
|
|
330
|
-
|
|
331
|
-
These ten sections may be omitted: `source_read`, `realtime_fetch`,
|
|
332
|
-
`abnormal_windows`, `target`, `prediction`, `parameter_relationships`,
|
|
333
|
-
`parameter_missing_policies`, `rowset`, `rowset_splits`, and `endpoint_policy`.
|
|
334
|
-
Omitting a section keeps older manifest hashes stable.
|
|
335
|
-
Sections containing only inert empty values are also treated as unset, but a
|
|
336
|
-
declared `realtime_fetch` policy contains meaningful defaults and changes the
|
|
337
|
-
manifest hash. Declaring `endpoint_policy` also changes the manifest hash.
|
|
338
|
-
|
|
339
|
-
Each explicit `parameters` request may set `missing_policy`. A
|
|
340
|
-
`parameter_missing_policies` entry is reserved for a Parameter used only through
|
|
341
|
-
the selected FeatureSet's input closure; it must not also appear in
|
|
342
|
-
`parameters`. Its exact V1 shape is `{"parameter": "<name>", "version":
|
|
343
|
-
"<version>", "project": "<project>", "policy": "<policy>"}`. Do not use
|
|
344
|
-
the `ParameterRequest` field names `name` or `missing_policy` in this list. For
|
|
345
|
-
a confirmed non-default target, set `project` explicitly instead of relying on
|
|
346
|
-
its `default` fallback. The policies are:
|
|
347
|
-
|
|
348
|
-
- `report_only`: retain all target rows and report source gaps.
|
|
349
|
-
- `fail_on_requested_range_gap`: fail when a detected gap affects the requested
|
|
350
|
-
target range.
|
|
351
|
-
- `drop_target_and_lookback`: remove the target rows in the detected window and
|
|
352
|
-
the following rows whose resolved Feature input lookback still reaches into
|
|
353
|
-
that window. The same merged row mask is applied to both
|
|
354
|
-
`feature_dataset.parquet` and `parameter_dataset.parquet`.
|
|
355
|
-
|
|
356
|
-
The default `report_only` value is omitted from canonical manifest identity so
|
|
357
|
-
manifests published before `missing_policy` existed keep their artifact hash.
|
|
358
|
-
When `endpoint_policy` is declared, `drop_target_and_lookback` is rejected:
|
|
359
|
-
endpoint eligibility requires the canonical datasets and
|
|
360
|
-
`rowset_membership.parquet` to retain the same complete candidate rowset. Use
|
|
361
|
-
`report_only` or `fail_on_requested_range_gap` for those manifests.
|
|
362
|
-
|
|
363
|
-
The resolved per-Parameter windows, policy, affected rows, and dropped rows are
|
|
364
|
-
written to `missing_windows.json` and summarized in `validation.json`. A
|
|
365
|
-
partitioned build with a drop policy must be materialized as one full source
|
|
366
|
-
window so the same global gap contract is used for every rowset.
|
|
367
|
-
|
|
368
|
-
`source_read` pages the source reads. A long target window read in one request
|
|
369
|
-
can exceed the driver read timeout; chunking keeps one manifest covering the
|
|
370
|
-
whole window instead of splitting it into per-day manifests. Chunk windows,
|
|
371
|
-
attempts, rows, and elapsed time land in
|
|
372
|
-
`latency_stats.fetch_timing_summary`.
|
|
373
|
-
|
|
374
|
-
```json
|
|
375
|
-
"source_read": {
|
|
376
|
-
"chunk_days": 1,
|
|
377
|
-
"timeout_seconds": 60,
|
|
378
|
-
"max_retries": 3,
|
|
379
|
-
"retry_sleep_seconds": 2
|
|
380
|
-
}
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
`realtime_fetch` declares the policy for one low-latency causal-cutoff read:
|
|
384
|
-
|
|
385
|
-
```json
|
|
386
|
-
"realtime_fetch": {
|
|
387
|
-
"max_workers": 4,
|
|
388
|
-
"min_lookback": "1h",
|
|
389
|
-
"validate_freshness": true,
|
|
390
|
-
"max_source_lag_hours": 3.0,
|
|
391
|
-
"max_source_lag_hours_by_parameter": {},
|
|
392
|
-
"allow_missing": false,
|
|
393
|
-
"allow_tail_edge_missing": true,
|
|
394
|
-
"max_tail_edge_missing": 1,
|
|
395
|
-
"tail_edge_missing_max_age_minutes": 65.0,
|
|
396
|
-
"allow_internal_gap_fill": true,
|
|
397
|
-
"internal_gap_fill_max_age_hours": 5.0
|
|
398
|
-
}
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
`POST /inference-data/fetch` serves one cutoff without running a batch build,
|
|
402
|
-
writing parquet, or publishing a DatasetArtifact. It derives
|
|
403
|
-
`target_time = cutoff_time + prediction.horizon`, filters every source to
|
|
404
|
-
`timestamp <= cutoff_time`, and derives each Parameter read window from the
|
|
405
|
-
selected Operators' `input_schema.history_requirements`. The response carries
|
|
406
|
-
`contract.manifest_hash` and a replayable `read_policy=as_of` manifest instead
|
|
407
|
-
of an artifact.
|
|
408
|
-
|
|
409
|
-
Keep the default strict: `allow_missing=false`. Tail-edge and bounded internal
|
|
410
|
-
gap-fill tolerance may return a row, but the response must mark
|
|
411
|
-
`freshness.degraded=true` and include validation evidence. A freshness or
|
|
412
|
-
missing-data condition that may recover returns HTTP `503`; an invalid contract
|
|
413
|
-
returns HTTP `400`. Declaring `realtime_fetch` changes the manifest hash; leaving
|
|
414
|
-
it unset preserves older contract hashes and uses environment fallbacks.
|
|
415
|
-
|
|
416
|
-
`abnormal_windows` excludes target windows whose data must not be trusted. The
|
|
417
|
-
policy and the lookback apply to the whole window set, not to individual
|
|
418
|
-
windows: a dataset is either a filtered rowset or a marked full rowset.
|
|
419
|
-
|
|
420
|
-
```json
|
|
421
|
-
"abnormal_windows": {
|
|
422
|
-
"policy": "drop_target_and_lookback",
|
|
423
|
-
"windows": [
|
|
424
|
-
{"start": "2026-05-25T19:30:00+08:00",
|
|
425
|
-
"end": "2026-05-26T09:50:00+08:00",
|
|
426
|
-
"reason": "shutdown"}
|
|
427
|
-
]
|
|
428
|
-
}
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
| policy | Meaning |
|
|
432
|
-
|-|-|
|
|
433
|
-
| `drop_target_and_lookback` | Drop rows in a window, and rows after it whose feature input lookback reaches back in. Default. |
|
|
434
|
-
| `drop_target_only` | Drop only rows inside a window. |
|
|
435
|
-
| `mark_only` | Drop nothing; record what is affected in lineage and validation. |
|
|
436
|
-
|
|
437
|
-
How far past a window to drop is derived from the widest resolved Operator history
|
|
438
|
-
requirement. Cutoff-relative history contributes `prediction.horizon + lookback`;
|
|
439
|
-
target-time-relative history contributes its declared lookback directly. The
|
|
440
|
-
maximum is used for source fetch and abnormal-window propagation. Do not restate
|
|
441
|
-
that number unless you want it asserted: an optional `input_lookback_hours` is
|
|
442
|
-
checked against the derived value and a mismatch is rejected. Note that a window
|
|
443
|
-
near the end of the target range can have its lookback clipped by the range end,
|
|
444
|
-
and a window close to the end can remove most remaining rows.
|
|
445
|
-
|
|
446
|
-
`target` states which instant the label is aligned to, so reviewing a manifest
|
|
447
|
-
shows the target definition rather than only a Parameter version string. When
|
|
448
|
-
the target is a Parameter, keep its complete versioned reference **inside**
|
|
449
|
-
`target.parameter`; do not flatten `version` or `project` onto `target`.
|
|
450
|
-
`si_time_source` is `weight_time` or `dispatch_time`. `interpolation` is an
|
|
451
|
-
object with a `method`, never a bare string.
|
|
452
|
-
|
|
453
|
-
```json
|
|
454
|
-
"target": {
|
|
455
|
-
"parameter": {
|
|
456
|
-
"parameter": "hot_metal_si",
|
|
457
|
-
"version": "v1",
|
|
458
|
-
"project": "replace_with_project"
|
|
459
|
-
},
|
|
460
|
-
"si_time_source": "weight_time",
|
|
461
|
-
"offset_minutes": 0,
|
|
462
|
-
"interpolation": {"method": "linear"}
|
|
463
|
-
}
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
Allowed interpolation methods are `none`, `linear`, and `forward_fill`. A
|
|
467
|
-
business phrase such as "allow and report target nulls" belongs in an existing
|
|
468
|
-
Parameter missing policy, Feature configuration, or the semantic review; it is
|
|
469
|
-
not a free-form `target` or DatasetManifest field.
|
|
470
|
-
|
|
471
|
-
The declaration is checked against the selected Si Parameter, but only for
|
|
472
|
-
`direct_column` reads, which name their time column in the contract. A `sql` or
|
|
473
|
-
`derived` Parameter can apply an offset inside its query text, so those are
|
|
474
|
-
recorded as unverified with a reason and a warning — do not read the absence of
|
|
475
|
-
an error as confirmation that the label instant matches.
|
|
476
|
-
|
|
477
|
-
`parameter_relationships` evaluates manifest-scoped assertions over exact
|
|
478
|
-
Parameter versions after preprocessing and grid alignment:
|
|
479
|
-
|
|
480
|
-
```json
|
|
481
|
-
"parameter_relationships": [
|
|
482
|
-
{
|
|
483
|
-
"id": "temperature_pair_consistency",
|
|
484
|
-
"inputs": [
|
|
485
|
-
{"alias": "left", "parameter": "temperature_left", "version": "v1"},
|
|
486
|
-
{"alias": "right", "parameter": "temperature_right", "version": "v1"}
|
|
487
|
-
],
|
|
488
|
-
"alignment": {"method": "last_before_or_at", "tolerance": "PT5M"},
|
|
489
|
-
"rules": [
|
|
490
|
-
{
|
|
491
|
-
"id": "bounded_difference",
|
|
492
|
-
"stage": "aligned_grid",
|
|
493
|
-
"check": {
|
|
494
|
-
"type": "absolute_difference",
|
|
495
|
-
"left": "left",
|
|
496
|
-
"right": "right",
|
|
497
|
-
"max_difference": 10.0
|
|
498
|
-
},
|
|
499
|
-
"acceptance": {"min_comparable_rate": 0.9},
|
|
500
|
-
"enforcement": "fail"
|
|
501
|
-
}
|
|
502
|
-
]
|
|
503
|
-
}
|
|
504
|
-
]
|
|
505
|
-
```
|
|
506
|
-
|
|
507
|
-
Relationship checks are `compare`, `absolute_difference`, `ratio_range`,
|
|
508
|
-
`sum_equals`, and `co_presence`. `exact` compares only exact grid timestamps.
|
|
509
|
-
`last_before_or_at` is causal. With a tolerance, each relationship input fetch
|
|
510
|
-
starts at `manifest.start - tolerance`; without one, lookup is bounded to data
|
|
511
|
-
inside the manifest target window and does not trigger an unbounded history read.
|
|
512
|
-
|
|
513
|
-
`rowset` selects which instants become dataset rows:
|
|
514
|
-
|
|
515
|
-
```json
|
|
516
|
-
"rowset": {
|
|
517
|
-
"strategy": "fixed_grid",
|
|
518
|
-
"grid": "10min"
|
|
519
|
-
}
|
|
520
|
-
```
|
|
521
|
-
|
|
522
|
-
Supported strategies are `event_driven`, `fixed_grid`, and
|
|
523
|
-
`exact_horizon_measured`. `fixed_grid` requires `grid`;
|
|
524
|
-
`exact_horizon_measured` requires `horizon` and accepts a `tolerance`. An
|
|
525
|
-
optional rowset `time_range` overrides the manifest range for that strategy.
|
|
526
|
-
Use `rowset_splits` instead of `rowset` when training needs named strategies
|
|
527
|
-
such as `support`, `validation`, and `test`; it is mutually exclusive with the
|
|
528
|
-
single `rowset`. Split ranges must not overlap and must cover every emitted
|
|
529
|
-
target row. A split-level `fixed_grid.grid` is an explicit assertion and must
|
|
530
|
-
equal `DatasetManifest.time_range.grid`; it cannot silently resample one split.
|
|
531
|
-
|
|
532
|
-
`rowset_splits` defines labels inside one dataset contract. It does not by
|
|
533
|
-
itself publish three DatasetManifests or three DatasetArtifacts. A build emits
|
|
534
|
-
one canonical Feature dataset and one Parameter dataset, with `rowset_split`
|
|
535
|
-
identifying each candidate row. Publish separate manifests only when separate
|
|
536
|
-
artifact identities, permissions, retention, or build lifecycles are required.
|
|
537
|
-
|
|
538
|
-
### Endpoint eligibility and membership
|
|
539
|
-
|
|
540
|
-
Use `endpoint_policy` when the platform must retain the complete canonical
|
|
541
|
-
rowset while declaring which target endpoints are safe for model training or
|
|
542
|
-
evaluation. Candidate construction and eligibility evaluation are two stages:
|
|
543
|
-
|
|
544
|
-
```text
|
|
545
|
-
time_range + rowset_splits -> candidate target rows and rowset labels
|
|
546
|
-
candidate rows + endpoint_policy -> eligibility membership and reasons
|
|
547
|
-
```
|
|
548
|
-
|
|
549
|
-
The policy is scoped to named rowsets, so one manifest can apply it to
|
|
550
|
-
`validation` and `test` while leaving `training` as pass-through:
|
|
551
|
-
|
|
552
|
-
```json
|
|
553
|
-
{
|
|
554
|
-
"prediction": {"horizon": "2h"},
|
|
555
|
-
"rowset_splits": {
|
|
556
|
-
"training": {
|
|
557
|
-
"strategy": "fixed_grid",
|
|
558
|
-
"grid": "10min",
|
|
559
|
-
"time_range": {
|
|
560
|
-
"start": "2025-01-01T00:00:00+08:00",
|
|
561
|
-
"end": "2026-01-01T00:00:00+08:00",
|
|
562
|
-
"grid": "10min"
|
|
563
|
-
}
|
|
564
|
-
},
|
|
565
|
-
"validation": {
|
|
566
|
-
"strategy": "fixed_grid",
|
|
567
|
-
"grid": "10min",
|
|
568
|
-
"time_range": {
|
|
569
|
-
"start": "2026-01-01T00:00:00+08:00",
|
|
570
|
-
"end": "2026-04-01T00:00:00+08:00",
|
|
571
|
-
"grid": "10min"
|
|
572
|
-
}
|
|
573
|
-
},
|
|
574
|
-
"test": {
|
|
575
|
-
"strategy": "fixed_grid",
|
|
576
|
-
"grid": "10min",
|
|
577
|
-
"time_range": {
|
|
578
|
-
"start": "2026-04-01T00:00:00+08:00",
|
|
579
|
-
"end": "2026-06-01T00:00:00+08:00",
|
|
580
|
-
"grid": "10min"
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
},
|
|
584
|
-
"abnormal_windows": {
|
|
585
|
-
"policy": "mark_only",
|
|
586
|
-
"windows": [
|
|
587
|
-
{
|
|
588
|
-
"start": "2026-03-20T00:00:00+08:00",
|
|
589
|
-
"end": "2026-03-28T00:00:00+08:00",
|
|
590
|
-
"reason": "shutdown"
|
|
591
|
-
}
|
|
592
|
-
]
|
|
593
|
-
},
|
|
594
|
-
"endpoint_policy": {
|
|
595
|
-
"scope": ["validation", "test"],
|
|
596
|
-
"policy": "exclude_target_or_input_overlap",
|
|
597
|
-
"anchor": "cutoff",
|
|
598
|
-
"context_lookback": "28h",
|
|
599
|
-
"window_source": "abnormal_windows",
|
|
600
|
-
"membership_column": "endpoint_eligible",
|
|
601
|
-
"emit_reason": true
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
```
|
|
605
|
-
|
|
606
|
-
`endpoint_policy` requires `prediction.horizon`, at least one abnormal window,
|
|
607
|
-
and `abnormal_windows.policy=mark_only`. Every `scope` name must exist in
|
|
608
|
-
`rowset_splits`; without splits, the only valid scope is `default`. The
|
|
609
|
-
membership column must not collide with reserved or materialized dataset
|
|
610
|
-
columns.
|
|
611
|
-
|
|
612
|
-
Do not derive `context_lookback` from an Operator automatically. Operator
|
|
613
|
-
history requirements answer how much source history is needed to compute a
|
|
614
|
-
Feature. `endpoint_policy.context_lookback` answers how much model input context
|
|
615
|
-
must be free of abnormal observations before an endpoint is usable. They can be
|
|
616
|
-
different and are versioned independently in the manifest contract.
|
|
617
|
-
|
|
618
|
-
For target time `t`, prediction horizon `h`, context lookback `L`, and abnormal
|
|
619
|
-
window `[a, b)`, evaluation uses:
|
|
620
|
-
|
|
621
|
-
```text
|
|
622
|
-
cutoff c = t - h
|
|
623
|
-
model context = [c - L, c]
|
|
624
|
-
target overlap = a <= t < b
|
|
625
|
-
context overlap = c >= a and c - L < b
|
|
626
|
-
eligible = not (target overlap or context overlap), within policy scope
|
|
627
|
-
```
|
|
628
|
-
|
|
629
|
-
The abnormal window is half-open and the cutoff side is closed. Its effective
|
|
630
|
-
exclusion tail ends at `b + h + L`; a target exactly at that instant is
|
|
631
|
-
eligible. A scoped row receives `target_in_abnormal_window`,
|
|
632
|
-
`input_context_overlap`, or both in `reason_codes`. Overlapping abnormal windows
|
|
633
|
-
do not duplicate a reason code, but all matching `window_id` values are retained.
|
|
634
|
-
|
|
635
|
-
The build does not delete ineligible rows. It writes
|
|
636
|
-
`rowset_membership.parquet` with one row per candidate target:
|
|
637
|
-
|
|
638
|
-
| Column | Meaning |
|
|
639
|
-
|-|-|
|
|
640
|
-
| `target_time` | Candidate model target time. |
|
|
641
|
-
| `cutoff_time` | `target_time - prediction.horizon`. |
|
|
642
|
-
| `rowset_name` | Named split, or `default` without `rowset_splits`. |
|
|
643
|
-
| `candidate` | Always true for a row emitted by the current candidate resolver. |
|
|
644
|
-
| `policy_applied` | Whether `rowset_name` belongs to `endpoint_policy.scope`. |
|
|
645
|
-
| `eligible` | Overlap result; pass-through rows outside scope remain true. |
|
|
646
|
-
| `reason_codes` | Stable list of exclusion reasons; empty when eligible or `emit_reason=false`. |
|
|
647
|
-
| `matched_window_ids` | Stable IDs of all matching abnormal windows. |
|
|
648
|
-
| `effective_exclusion_end` | Latest matched window end plus horizon and context lookback. |
|
|
649
|
-
| `policy_hash` | Hash of the resolved endpoint policy and normalized windows. |
|
|
650
|
-
| `manifest_hash` | Dataset contract identity used for the build. |
|
|
651
|
-
|
|
652
|
-
For compatibility, `feature_dataset.parquet` and `parameter_dataset.parquet`
|
|
653
|
-
also receive the configured `membership_column`. Its value is:
|
|
654
|
-
|
|
655
|
-
```text
|
|
656
|
-
endpoint_eligible = policy_applied AND eligible
|
|
657
|
-
```
|
|
658
|
-
|
|
659
|
-
Consequently, a row outside `scope` is a pass-through membership row with
|
|
660
|
-
`eligible=true`, but its compatibility column is false. Use the membership
|
|
661
|
-
artifact when consumers need to distinguish "not evaluated" from "evaluated
|
|
662
|
-
and rejected"; do not interpret the compatibility column alone as a global
|
|
663
|
-
data-quality flag.
|
|
664
|
-
|
|
665
|
-
The resolved manifest, `quality_report.json`, `validation.json`, and lineage
|
|
666
|
-
record the resolved policy and counts. Reconcile at least
|
|
667
|
-
`candidate_rows`, `policy_applied_rows`, `evaluated_eligible_rows`,
|
|
668
|
-
`ineligible_rows`, `pass_through_rows`, `reason_counts`, and per-rowset counts.
|
|
669
|
-
`eligible_rows` includes true pass-through rows, whereas
|
|
670
|
-
`evaluated_eligible_rows` counts only scoped eligible rows. Single and
|
|
671
|
-
partitioned builds of the same manifest must produce equivalent merged
|
|
672
|
-
membership, counts, lineage, and `policy_hash`.
|
|
673
|
-
|
|
674
|
-
For the EXP301 validation contract, `h=2h` and `L=28h`, so the exclusion tail is
|
|
675
|
-
30 hours after each abnormal-window end. The fixed 10-minute validation rowset
|
|
676
|
-
contains 20,853 candidates: 19,263 are evaluated eligible and 1,590 are
|
|
677
|
-
ineligible. Treat those figures as an example-specific regression contract,
|
|
678
|
-
not platform defaults.
|
|
679
|
-
|
|
680
|
-
`endpoint_policy` selects historical dataset rows and is not applied to
|
|
681
|
-
`POST /inference-data/fetch`. Realtime inference reports it as not applicable
|
|
682
|
-
and omits it from the replay manifest.
|
|
683
|
-
|
|
684
|
-
The current public schema still records `output.engine=chronon`, while the first-phase bridge executes Python Feature Operators and does not run Chronon compile/backfill. Report this limitation accurately.
|
|
685
|
-
|
|
686
|
-
## Catalog
|
|
687
|
-
|
|
688
|
-
```json
|
|
689
|
-
{
|
|
690
|
-
"schema_version": "ml_data_platform.catalog/v1",
|
|
691
|
-
"name": "pressure_feature_catalog",
|
|
692
|
-
"version": "v1",
|
|
693
|
-
"parameters": ["parameters/hot_blast_pressure.v1.json"],
|
|
694
|
-
"operators": [
|
|
695
|
-
{
|
|
696
|
-
"spec": "operators/pressure_features.v1.json",
|
|
697
|
-
"package": "operator_package/dist/pressure_features-1.0.0-py3-none-any.whl"
|
|
698
|
-
}
|
|
699
|
-
],
|
|
700
|
-
"features": ["features/pressure_mean_5m.v1.json"],
|
|
701
|
-
"feature_sets": ["feature_sets/pressure_core.v1.json"],
|
|
702
|
-
"datasets": ["datasets/pressure_training_20260701_20260702.v1.json"]
|
|
703
|
-
}
|
|
704
|
-
```
|
|
705
|
-
|
|
706
|
-
All paths must be unique, relative, remain inside the catalog directory, exist, and have the expected `.json` or `.whl` suffix.
|
|
707
|
-
|
|
708
|
-
## Cross-Object Validation
|
|
709
|
-
|
|
710
|
-
Require:
|
|
711
|
-
|
|
712
|
-
- every Feature input Parameter to exist in the target Registry or the same catalog;
|
|
713
|
-
- every Feature Operator to exist and declare `type=feature`;
|
|
714
|
-
- every Feature input to be allowed by the Operator input schema when that schema declares parameters;
|
|
715
|
-
- every Feature physical output to be declared by the Operator output schema;
|
|
716
|
-
- every FeatureSet reference to exist;
|
|
717
|
-
- every Dataset FeatureSet and preprocess Operator to exist;
|
|
718
|
-
- every Dataset relationship input Parameter to exist;
|
|
719
|
-
- every new immutable version to differ by key from conflicting registered content.
|