Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
134 commits
Select commit Hold shift + click to select a range
5069de2
Read the holdout of the published model, not the last-evaluated one
Vedaant-J Oct 21, 2026
ce86b46
Expose published_fit_mode on the artifact test's fake trainer
Vedaant-J Oct 22, 2026
c1586bd
Record a snapshot manifest next to every graph index
Vedaant-J Oct 20, 2026
7d6ecbe
Address snapshot manifest review feedback
Vedaant-J Oct 20, 2026
2e2b01d
Match the backfill diff when no build commit is recorded
Vedaant-J Oct 20, 2026
244b5fe
Record each source table's full location in the snapshot manifest
Vedaant-J Oct 21, 2026
9eff66d
Drop graph_id and data_version from the snapshot manifest; keep the d…
Vedaant-J Oct 21, 2026
aeb0b22
Tighten manifest schema checks and scheduler context
Vedaant-J Oct 21, 2026
16b844f
Drop snapshot-manifest tests of private helpers
Vedaant-J Oct 21, 2026
048473f
Trim snapshot manifest PR to essentials
Vedaant-J Oct 21, 2026
0ecb465
Reuse existing helpers in the snapshot manifest
Vedaant-J Oct 21, 2026
2552bc1
Record DiskGraph's version and build commit in manifests
Vedaant-J Oct 21, 2026
8b16cd1
Type source versions and allow location-free snapshot manifests
Vedaant-J Oct 21, 2026
482b1b2
Pick the source-location lookup by connector type
Vedaant-J Oct 21, 2026
0d73b76
Record Custom SQL text by default; one record_sources switch
Vedaant-J Oct 21, 2026
d2cd1c0
Stamp the engine build commit into wheels; type directory versions
Vedaant-J Oct 21, 2026
72e2932
Carry the record_sources choice on GraphSnapshot
Vedaant-J Oct 22, 2026
60e1ab6
Record Custom SQL's namespace and normalized fingerprint
Vedaant-J Oct 22, 2026
915e10c
Make the snapshot manifest a frozen dataclass
Vedaant-J Oct 22, 2026
bd39c7c
Record training-table lineage across export and reload
Vedaant-J Oct 20, 2026
e9778ae
Digest exported parts at the source for training-table lineage
Vedaant-J Oct 20, 2026
0a49692
Record a custom table's source instead of re-reading its export
Vedaant-J Oct 21, 2026
cf7fea2
Record a custom table's source in the snapshot manifest's shape
Vedaant-J Oct 21, 2026
209e6ee
Keep sidecar writes best-effort; version custom tables from their read
Vedaant-J Oct 21, 2026
80807de
Trim training-table lineage to essentials
Vedaant-J Oct 21, 2026
fcde71e
Read row counts and source mode from DiskGraph's manifest
Vedaant-J Oct 21, 2026
bba451d
Record a custom table's source as the snapshot's manifest does
Vedaant-J Oct 21, 2026
aeab584
Export a training table even when its manifest sidecar fails
Vedaant-J Oct 21, 2026
370351b
Follow the snapshot's record_sources choice
Vedaant-J Oct 21, 2026
f266ec5
Expect the listing kind for a reloaded export directory
Vedaant-J Oct 21, 2026
23f57a2
Record a custom table's source only when the snapshot did
Vedaant-J Oct 22, 2026
f7c62c3
Make the training-table manifest a frozen dataclass
Vedaant-J Oct 22, 2026
d867b57
Record bundle_id and fit provenance in every model bundle
Vedaant-J Oct 20, 2026
4ced958
Address bundle provenance review feedback
Vedaant-J Oct 20, 2026
51c767a
Read Trainer.bundle_id defensively
Vedaant-J Oct 20, 2026
8047610
Record the published model's fit mode in provenance.json
Vedaant-J Oct 20, 2026
f03673d
Mirror the training table's source in bundle provenance
Vedaant-J Oct 21, 2026
5f3dbef
Trim provenance.json to facts no other bundle file holds
Vedaant-J Oct 21, 2026
777bdb4
Record fit_mode and source versions in provenance; expose it on Trainer
Vedaant-J Oct 21, 2026
6eab1d6
Trim bundle provenance to essentials
Vedaant-J Oct 21, 2026
1321f3c
Reuse shared helpers for bundle provenance
Vedaant-J Oct 21, 2026
ef4485c
Compute a fresh bundle's id from its publication manifest
Vedaant-J Oct 21, 2026
fd6e8d7
Copy a custom table's full source entry into provenance
Vedaant-J Oct 21, 2026
e0ad8ac
Store the bundle id in provenance.json
Vedaant-J Oct 21, 2026
4274048
Make bundle provenance a frozen dataclass
Vedaant-J Oct 22, 2026
160f8a6
Add GraphSnapshot export and load through object storage
Vedaant-J Oct 20, 2026
62b034b
Ship the object-store sink in the public connector image
Vedaant-J Oct 20, 2026
4cd042e
Report source changes since a snapshot and warn on stale loads
Vedaant-J Oct 20, 2026
dee6bb2
Match source changes on the recorded connector and location
Vedaant-J Oct 21, 2026
59d62fa
Keep an existing export when a rebuilt snapshot is re-exported
Vedaant-J Oct 21, 2026
ef1a4ad
Pass allow_delayed_open to S3 only when pyarrow supports it
Vedaant-J Oct 21, 2026
a58e325
Trim comments and implementation-detail tests
Vedaant-J Oct 21, 2026
4c9f341
Trim snapshot export PR to essentials
Vedaant-J Oct 21, 2026
6ca9017
Move snapshot export checksums into DiskGraph and reuse its manifest
Vedaant-J Oct 21, 2026
f8563b4
Check loaded snapshots with what DiskGraph already stamps
Vedaant-J Oct 21, 2026
a51952c
Derive GraphSnapshot.graph_key from the snapshot id
Vedaant-J Oct 21, 2026
660c41b
Make snapshot export atomic and pass the snapshot manifest through
Vedaant-J Oct 21, 2026
b583a96
Drop the S3 upload tuning from snapshot export
Vedaant-J Oct 21, 2026
85676da
Repair files a concurrent export wiped before committing
Vedaant-J Oct 21, 2026
f37a3bd
Compare directory listings in source_changes
Vedaant-J Oct 21, 2026
c82556a
Drop the connector-image allowlist entry for the removed sink test
Vedaant-J Oct 21, 2026
48f5796
Raise ConcurrentExportError when another export keeps replacing the d…
Vedaant-J Oct 22, 2026
b6394e2
Read snapshot manifests as dataclasses in export and source_changes
Vedaant-J Oct 22, 2026
d4272b5
Add PredictiveQuery.from_snapshot for graph-free scoring
Vedaant-J Oct 20, 2026
eb0f0db
Expose Trainer.training_query and keep the graph-bound snapshot check
Vedaant-J Oct 21, 2026
568ce32
Trim comments and implementation-detail tests
Vedaant-J Oct 21, 2026
3df8621
Show where a graph-free job gets its snapshot
Vedaant-J Oct 21, 2026
8d23970
Read the native graph and graph id from GraphSnapshot
Vedaant-J Oct 21, 2026
975d144
Add Trainer.predict_snapshot for one-call graph-free scoring
Vedaant-J Oct 21, 2026
2c619ea
Distinguish graph-free scoring in the model reload docs
Vedaant-J Oct 21, 2026
2cc4e5c
Test graph-free scoring of an exported snapshot end to end
Vedaant-J Oct 21, 2026
49bae78
Warn when predict_snapshot scores another graph's snapshot
Vedaant-J Oct 22, 2026
6bdd795
Report fit progress events to callbacks and JSON-lines stdout
Vedaant-J Oct 20, 2026
4c09c57
Bind the trial error name before the progress-event closure
Vedaant-J Oct 20, 2026
bcc7503
Drop the library JSON progress option; callbacks cover it
Vedaant-J Oct 21, 2026
936f289
Report trial_ended only once the trial is recorded as succeeded
Vedaant-J Oct 21, 2026
eebc54a
Carry the error type and a short message on failure events
Vedaant-J Oct 21, 2026
4e7502b
Trim internal docstrings and implementation-detail test checks
Vedaant-J Oct 21, 2026
c1b3e24
Trim fit progress events to essentials
Vedaant-J Oct 21, 2026
18e4d3f
Keep fit_started best-effort when the inputs are malformed
Vedaant-J Oct 21, 2026
8197f42
Reuse the progress logger, searcher and instrumentation for fit events
Vedaant-J Oct 21, 2026
6b5f8eb
Never fail a fit when an epoch event cannot be built
Vedaant-J Oct 21, 2026
6dea3f7
Record Trainer.fit in MLflow with opt-in fit(tracking=)
Vedaant-J Oct 20, 2026
0dfa5d6
Move tracking names into core run records; thin MLflow adapter
Vedaant-J Oct 21, 2026
8473c1d
Keep record building best-effort; declare MLflow's Lightning step axis
Vedaant-J Oct 21, 2026
24bc0ef
Tag model links in core; log per-epoch steps and finish tags in MLflow
Vedaant-J Oct 21, 2026
e61dc62
Drop the removed progress setting from the MLflow test fixture
Vedaant-J Oct 21, 2026
ef0ed18
Record run kind and failures as tags; keep ids out of params
Vedaant-J Oct 21, 2026
a8317fb
Move the tracking adapter API to kumo.engine.tracking
Vedaant-J Oct 21, 2026
00987ae
Stop calling a tracker for the rest of the fit after it raises
Vedaant-J Oct 21, 2026
9972e15
Trim tracking docstrings and tests of internals
Vedaant-J Oct 21, 2026
18a973d
Trim MLflow tracking to essentials
Vedaant-J Oct 21, 2026
5d1c1a5
Build tracking records once in the session; adapters get records
Vedaant-J Oct 21, 2026
8d829a7
Keep experiment tracking off the training thread
Vedaant-J Oct 21, 2026
469742d
Drop the training table id from fit runs
Vedaant-J Oct 21, 2026
529e09c
Record pointers, not lineage, on tracked fit runs
Vedaant-J Oct 21, 2026
9892fc2
Log source tables as native MLflow inputs; cut unused tracking surface
Vedaant-J Oct 21, 2026
2686f5c
Log manifest source tables as MLflow inputs whatever the connector case
Vedaant-J Oct 22, 2026
c9ff49f
Require MLflow 3.13 in the mlflow extra
Vedaant-J Oct 22, 2026
5a78d42
Expose the fit run's id as MlflowTracking.run_id
Vedaant-J Oct 22, 2026
6c1943c
Read fit inputs through the manifest dataclasses
Vedaant-J Oct 22, 2026
b05926f
Record Trainer.fit in Snowflake Experiment Tracking
Vedaant-J Oct 21, 2026
f5eb73f
Expose the fit run's name as SnowflakeTracking.run_id
Vedaant-J Oct 22, 2026
b5b405e
Carry scoring defaults in a derived model bundle
Vedaant-J Oct 20, 2026
75378e4
Normalise scoring defaults and allow a multilabel threshold default
Vedaant-J Oct 21, 2026
f670d50
Trim scoring defaults to essentials
Vedaant-J Oct 21, 2026
fe4ec2a
Reuse bundle_task_type and write_manifest for scoring defaults
Vedaant-J Oct 21, 2026
b8e8d9e
Record a derived bundle's own id in its provenance.json
Vedaant-J Oct 21, 2026
248091b
Record bundle derivation on the provenance dataclass
Vedaant-J Oct 22, 2026
c606def
Accept trailing-slash directories when fetching Databricks models
Vedaant-J Oct 21, 2026
0a07a2d
Add model aliases and a bundle id pointer to model registries
Vedaant-J Oct 21, 2026
18e3b42
Roll back failed Snowflake alias moves; validate aliases before fit
Vedaant-J Oct 21, 2026
f08aeee
Add prediction_output_columns and connector_for_root helpers
Vedaant-J Oct 21, 2026
0b693a1
Log, register and serve fitted models through MLflow pyfunc
Vedaant-J Oct 21, 2026
136d779
Score each pyfunc predict in its own scratch dir; state MLflow 3.13
Vedaant-J Oct 22, 2026
bb34a54
Bound the model-logging wait; give only the upload a longer timeout
Vedaant-J Oct 22, 2026
5255ad9
Read provenance and scheduler context as dataclasses for MLflow
Vedaant-J Oct 22, 2026
4e852aa
Add kumo.job() for scheduled job scripts
Vedaant-J Oct 21, 2026
0a788b2
Print the job summary in finally; exit 0 or non-zero
Vedaant-J Oct 21, 2026
a792a18
Keep at most 20 distinct, truncated warnings in the job summary
Vedaant-J Oct 21, 2026
235a677
Leave deprecation warnings out of the job summary
Vedaant-J Oct 21, 2026
b5d56e0
Do not recurse when another patcher wraps kumo.job()'s showwarning
Vedaant-J Oct 22, 2026
2c18051
Write the job summary's scheduler from SchedulerContext
Vedaant-J Oct 22, 2026
3b21bbe
Add SnowflakeConnector.session() for the connector's Snowpark session
Vedaant-J Oct 21, 2026
5a6c058
Add Databricks, SageMaker and Snowflake job templates for kumo-engine
Vedaant-J Oct 21, 2026
4379a7d
Score from the fit's model path; mark Snowflake template experimental
Vedaant-J Oct 21, 2026
bf5c05d
Record full output URIs in job summaries; update SageMaker notes
Vedaant-J Oct 21, 2026
e180ade
Note the score job's missing lineage columns and prediction run
Vedaant-J Oct 21, 2026
89c4bdf
Run job scripts without __file__; install requirements in the bundle
Vedaant-J Oct 21, 2026
b52c03f
Report no model version for a fit published without a registry
Vedaant-J Oct 22, 2026
67fd1a9
Note the torchvision pin needed on a Databricks ML runtime
Vedaant-J Oct 22, 2026
7966009
Document standard DBR runtime for the Databricks bundle
Vedaant-J Oct 22, 2026
f21cf92
Use a CPU node type in the Databricks bundle
Vedaant-J Oct 22, 2026
8737197
Add opt-in lineage columns, stamped by the prediction writer
Vedaant-J Oct 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion 3rdparty/notices/inventory.json
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,7 @@
"rust:cpp-demangle@0.4.5",
"rust:cpufeatures@0.2.17",
"rust:cpufeatures@0.3.0",
"rust:crc32fast@1.5.0",
"rust:crossbeam-deque@0.8.6",
"rust:crossbeam-epoch@0.9.21",
"rust:crossbeam-utils@0.8.21",
Expand Down Expand Up @@ -3642,7 +3643,8 @@
},
{
"artifact_ids": [
"diskgraph-extension"
"diskgraph-extension",
"diskgraph-ingest"
],
"copyright": [
"Copyright (c) 2018 Sam Rijs, Alex Crichton and contributors"
Expand Down
2 changes: 1 addition & 1 deletion THIRD-PARTY.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 61 additions & 0 deletions examples/mlops/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Scheduled kumo-engine jobs

Two plain Python job scripts that use the normal `kumo.engine` API inside
`kumo.job()`, and the wiring that runs them on a schedule:

- `fit_job.py` builds a snapshot (exported under `KUMO_ARTIFACT_ROOT` when
set), trains the users-churn model, publishes it and moves the `champion`
alias to it.
- `score_job.py` loads `champion`, scores a fresh snapshot (or the exported
one named by `KUMO_SNAPSHOT`) with `ANCHOR_TIMESTAMP` on every row, and
writes the predictions. It passes a 0.5 threshold unless the model carries
scoring defaults.
- `jobs.py` holds what both share: the graph (users, items, orders), the
connectors and the registry choice, all read from environment variables
(listed in its docstring). Replace it with your own module.

| Directory | Platform | Registry | Schedule |
|---|---|---|---|
| [databricks](databricks/README.md) | Databricks Jobs (Asset Bundle) | Unity Catalog through MLflow | train on demand, score daily |
| [sagemaker](sagemaker/README.md) | SageMaker Pipelines | SageMaker managed MLflow | fit step, then score step |
| [snowflake](snowflake/README.md) (experimental, preview APIs) | Snowflake ML Jobs + Tasks | Snowflake Model Registry, Snowflake Experiment Tracking | train weekly, score daily |

## Run locally

With `users.parquet`, `items.parquet` and `orders.parquet` in a directory:

```bash
cd examples/mlops
export KUMO_SOURCE=file KUMO_DATA_ROOT=<data dir> KUMO_ARTIFACT_ROOT=<artifact dir>
python fit_job.py --job-id local --job-run-id run-1
KUMO_MODEL_PATH=runs/run-1/model python score_job.py --job-id local --job-run-id run-2
```

Without `KUMO_REGISTRY` the fit publishes the model to
`<artifact dir>/runs/<fit run>/model` (its summary's `outputs.model.uri`; the
`version` there is null, as no registry assigned one),
and the score job loads the path `KUMO_MODEL_PATH` names under
`KUMO_ARTIFACT_ROOT`; scheduled score runs have their own run ids, so set it to
the fit run you want to score with. The query needs
several 30-day windows of order history; for a small sample set `KUMO_QUERY`
to a shorter window and `KUMO_NUM_EXPERIMENTS=1`.

## What every scheduler sees

- **Exit codes.** `0` ok, `1` failure, `143` SIGTERM (timeout or cancel).
The summary's `error.kind` says whether a failure is `transient` (worth a
retry), `permanent` or `unknown`.
- **Output.** Each script prints one JSON line on stdout when it ends:
`status`, `exit_code`, `scheduler`, `bundle_id`, `snapshot_id`, `run_id`,
`outputs` (full URIs of the exported snapshot and published model, or of
the prediction destinations), `warnings` and `error`. Logs go to stderr.
- **Job ids.** `--job-id`/`--job-run-id` (or `KUMO_JOB_ID`/`KUMO_JOB_RUN_ID`)
are recorded as `scheduler: {platform, job_id, job_run_id}` in the snapshot
and training-table manifests and the bundle's `provenance.json`, and as
`kumo.scheduler`/`kumo.job_id`/`kumo.job_run_id` on a tracked fit run.
Without them the engine detects SageMaker and Snowflake ML Jobs itself.

## Limitations

`score_job.py` does not yet tag output rows with lineage columns or record a
tracked prediction run; both arrive with the prediction-run PRs.
85 changes: 85 additions & 0 deletions examples/mlops/databricks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Databricks Jobs

`databricks.yml` is an Asset Bundle with two jobs on a single-node CPU job
cluster. Each task is a `spark_python_task` running a job script with
`--job-id {{job.id}} --job-run-id {{job.run_id}}`; the cluster's
`spark_env_vars` configure `jobs.py`.

- **`kumo_train`** runs `fit_job.py` on the `users`, `items` and `orders`
tables in `<catalog>.<schema>`. `KUMO_REGISTRY=mlflow` logs the fit to
`MLFLOW_EXPERIMENT_NAME`, registers the model as a new version of
`<catalog>.<schema>.users_churn` and moves the `champion` alias to it. The
snapshot is exported to the volume.
- **`kumo_score`** runs `score_job.py` daily at 03:00 UTC (created paused).
It loads `models:/<catalog>.<schema>.users_churn@champion`, builds a fresh
snapshot and writes `users_churn_predictions` in the connector's schema. Set
`KUMO_SNAPSHOT=snapshots/<id>` to score an exported snapshot instead.

## Before deploying

1. Put the kumo-engine wheel and a `requirements.txt` for its dependencies
(kumo-core, kumo-api, kumo-pql, diskgraph, kumo-databricks, mlflow 3) on a
volume and set `kumo_engine_wheel` and `requirements`. Start the
requirements file with `--prefer-binary`: DBR 16.4's setuptools cannot
build the SQLAlchemy sdist MLflow pulls. Each task installs the
requirements, then the wheel.
Use a standard (non-ML) DBR runtime: the ML runtime's preinstalled
torch, torchvision and pyOpenSSL conflict with kumo-engine's pins.
Databricks GPU nodes require a GPU ML runtime, so use a CPU node
type with local NVMe disks (for example m5d.xlarge).
2. Replace the `<...>` placeholders in `databricks.yml` (or pass
`--var catalog=...`). The parent folder of the MLflow experiment
path must already exist in the workspace.
3. Adjust the `dev` target (workspace `root_path`) or add your own, then
`databricks bundle validate` and `databricks bundle deploy -t <target>`. The bundle syncs this
whole `examples/mlops` directory (`sync.paths: [..]`).

## Privileges for the run-as principal

- `USE CATALOG`, `USE SCHEMA` and `SELECT` on the three tables; `CREATE TABLE`
and `MODIFY` on the schema for predictions.
- `READ VOLUME`/`WRITE VOLUME` on `<volume>`, and `CREATE VOLUME` on the
schema (or a pre-created `<catalog>.<schema>._kumo_staging` volume) for
staged table writes.
- `CAN USE` on the SQL warehouse; edit permission on the MLflow experiment.
- `CREATE MODEL` on the schema and `EXECUTE` on the model; moving an alias
needs rights on the model itself.

## Notes

- **Script, not `python_wheel_task`.** Databricks overrides a custom
`PYTHONPATH` at driver startup, so a wheel task cannot import `jobs.py`; the
scripts put their own directory on `sys.path` (from `sys.argv[0]`: a
`spark_python_task` runs the file without `__file__`). Databricks sets no job-id
environment variable, so ids come from `{{job.id}}`/`{{job.run_id}}`.
- **Dedicated access mode** (`SINGLE_USER`): standard compute runs as a
low-privilege user and has no GPUs. `KUMO_SCRATCH_DIR` is
`/local_disk0/kumo`.
- **MLflow.** `MLFLOW_TRACKING_URI=databricks` and
`MLFLOW_REGISTRY_URI=databricks-uc` are set explicitly;
`MLFLOW_EXPERIMENT_NAME` is required in jobs.
- **One registry format.** A `DatabricksConnector` publish and an MLflow
pyfunc model are different formats; use one per registered model.
- **Job clusters.** Both jobs repeat the cluster block; YAML anchors would
remove the copy but are untested with `databricks bundle validate`.

## Verified

The bundle was deployed and both jobs were run on a single-node cluster against
synthetic `users`, `items` and `orders` tables: both exit 0 with the JSON
summary and the Databricks job and run ids, the fit registers a new
`users_churn` version and moves `champion`, and the score job loads it and
writes the predictions table.

## References

- Task types and parameters:
https://docs.databricks.com/aws/en/dev-tools/bundles/job-task-types
- Custom `PYTHONPATH` overridden:
https://kb.databricks.com/en_US/clusters/cannot-set-a-custom-pythonpath
- Dynamic value references:
https://docs.databricks.com/aws/en/jobs/dynamic-value-references
- Standard compute limitations:
https://docs.databricks.com/aws/en/compute/standard-limitations
- Unity Catalog models:
https://docs.databricks.com/aws/en/machine-learning/manage-model-lifecycle/
136 changes: 136 additions & 0 deletions examples/mlops/databricks/databricks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# Train and daily score jobs for kumo-engine; see README.md.

bundle:
name: kumo-engine-users-churn

sync:
paths:
- ..

variables:
catalog:
description: Unity Catalog catalog holding the users, items and orders tables
default: <catalog>
schema:
description: schema holding the tables, the registered model and the predictions
default: <schema>
volume:
description: Unity Catalog volume for exported models and snapshots
default: <volume>
warehouse_id:
description: SQL warehouse that reads the source tables and writes predictions
default: <warehouse_id>
kumo_engine_wheel:
description: kumo-engine wheel on a volume or workspace path
default: /Volumes/<catalog>/<schema>/<volume>/wheels/<kumo_engine_wheel>.whl
requirements:
description: requirements.txt for kumo-engine's dependencies, starting with --prefer-binary
default: /Volumes/<catalog>/<schema>/<volume>/wheels/requirements.txt
spark_version:
description: a standard (non-ML) runtime, for example 16.4.x-scala2.12
default: <dbr_version>
node_type_id:
description: a CPU node type with local NVMe disks, for example m5d.xlarge
default: <node_type>
experiment:
description: MLflow experiment (workspace path) for train and score runs
default: /Shared/kumo-engine/users-churn

resources:
jobs:
kumo_train:
name: kumo-engine-train
max_concurrent_runs: 1
job_clusters:
- job_cluster_key: kumo
new_cluster:
spark_version: ${var.spark_version}
node_type_id: ${var.node_type_id}
num_workers: 0
data_security_mode: SINGLE_USER
spark_conf:
spark.databricks.cluster.profile: singleNode
spark.master: local[*]
custom_tags:
ResourceClass: SingleNode
spark_env_vars:
KUMO_SOURCE: databricks
KUMO_DATABRICKS_CATALOG: ${var.catalog}
KUMO_DATABRICKS_SCHEMA: ${var.schema}
KUMO_DATABRICKS_WAREHOUSE_ID: ${var.warehouse_id}
KUMO_ARTIFACT_ROOT: /Volumes/${var.catalog}/${var.schema}/${var.volume}/kumo
MLFLOW_TRACKING_URI: databricks
MLFLOW_REGISTRY_URI: databricks-uc
MLFLOW_EXPERIMENT_NAME: ${var.experiment}
KUMO_REGISTRY: mlflow
KUMO_SCRATCH_DIR: /local_disk0/kumo
KUMO_MODEL_NAME: ${var.catalog}.${var.schema}.users_churn
tasks:
- task_key: fit
job_cluster_key: kumo
libraries:
- requirements: ${var.requirements}
- whl: ${var.kumo_engine_wheel}
spark_python_task:
python_file: ${workspace.file_path}/fit_job.py
parameters:
- --job-id
- "{{job.id}}"
- --job-run-id
- "{{job.run_id}}"

kumo_score:
name: kumo-engine-score
max_concurrent_runs: 1
schedule:
quartz_cron_expression: 0 0 3 * * ?
timezone_id: UTC
pause_status: PAUSED
job_clusters:
- job_cluster_key: kumo
new_cluster:
spark_version: ${var.spark_version}
node_type_id: ${var.node_type_id}
num_workers: 0
data_security_mode: SINGLE_USER
spark_conf:
spark.databricks.cluster.profile: singleNode
spark.master: local[*]
custom_tags:
ResourceClass: SingleNode
spark_env_vars:
KUMO_SOURCE: databricks
KUMO_DATABRICKS_CATALOG: ${var.catalog}
KUMO_DATABRICKS_SCHEMA: ${var.schema}
KUMO_DATABRICKS_WAREHOUSE_ID: ${var.warehouse_id}
KUMO_ARTIFACT_ROOT: /Volumes/${var.catalog}/${var.schema}/${var.volume}/kumo
MLFLOW_TRACKING_URI: databricks
MLFLOW_REGISTRY_URI: databricks-uc
MLFLOW_EXPERIMENT_NAME: ${var.experiment}
KUMO_REGISTRY: mlflow
KUMO_SCRATCH_DIR: /local_disk0/kumo
KUMO_MODEL_NAME: ${var.catalog}.${var.schema}.users_churn
KUMO_PREDICTIONS_TABLE: users_churn_predictions
tasks:
- task_key: score
job_cluster_key: kumo
libraries:
- requirements: ${var.requirements}
- whl: ${var.kumo_engine_wheel}
spark_python_task:
python_file: ${workspace.file_path}/score_job.py
parameters:
- --job-id
- "{{job.id}}"
- --job-run-id
- "{{job.run_id}}"

targets:
dev:
default: true
workspace:
root_path: /Workspace/Shared/kumo-engine/.bundle/${bundle.name}/${bundle.target}
59 changes: 59 additions & 0 deletions examples/mlops/fit_job.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""Train and publish the users-churn model: ``python fit_job.py [--job-id ID
--job-run-id ID]``; see README.md."""

import os
import sys
from dataclasses import asdict
from typing import Optional, Sequence

sys.path.insert(0, os.path.dirname(os.path.abspath(
globals().get('__file__') or sys.argv[0])))

import jobs # noqa: E402

import kumo.engine as kumo # noqa: E402

# Needs several 30-day windows of order history; shorten it for small data.
QUERY = 'PREDICT COUNT(orders.*, 0, 30, days) > 0 FOR EACH users.USER_ID'


def main(argv: Optional[Sequence[str]] = None) -> None:
job_id, job_run_id = jobs.start(argv)
with kumo.job(job_id=job_id, job_run_id=job_run_id) as run:
connector = jobs.source()
graph = jobs.build_graph(connector)
snapshot = graph.snapshot()
run.record(snapshot_id=snapshot.id)
if os.environ.get('KUMO_ARTIFACT_ROOT'):
run.record(outputs={
'snapshot':
jobs.artifact_uri(snapshot.export(jobs.artifacts(), 'snapshots'))
})

query = kumo.PredictiveQuery(graph=graph,
query=os.environ.get('KUMO_QUERY', QUERY))
table = query.generate_training_table(snapshot)
plan = query.suggest_model_plan(snapshot)
if os.environ.get('KUMO_NUM_EXPERIMENTS'):
plan.training_job.num_experiments = int(
os.environ['KUMO_NUM_EXPERIMENTS'])
tracking, export_config = jobs.model_destination(connector)
result = kumo.Trainer(plan).fit(table, snapshot=snapshot,
tracking=tracking,
export_config=export_config)

registered = getattr(tracking, 'registered', None)
published = registered or result.published_model
model = published and asdict(published)
if model and not os.environ.get('KUMO_REGISTRY'):
model['version'] = None
run.record(bundle_id=result.bundle_id,
run_id=getattr(tracking, 'run_id', None),
outputs={'model': model})


if __name__ == '__main__':
main()
Loading
Loading