Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
338 commits
Select commit Hold shift + click to select a range
240bd48
Merge #50's review fixes into #60
Vedaant-J Oct 30, 2026
6763e74
Refuse an occupied output directory before scoring, in FilesystemSink
Vedaant-J Oct 31, 2026
1c8e82a
Merge #112's review fixes into #114
Vedaant-J Oct 31, 2026
85b8b06
Refuse an occupied volume directory whatever the write mode, through …
Vedaant-J Oct 30, 2026
cceb78e
Drop the unused write_files options passthrough
Vedaant-J Oct 30, 2026
dc1405f
Shorten the volume sink docstrings
Vedaant-J Oct 30, 2026
a7fdac9
Tidy the volume handler: one-line docs, top-level imports, no custom …
Vedaant-J Oct 30, 2026
973dc8a
Show the chunk generation directory in the volume README
Vedaant-J Oct 30, 2026
53d23fc
Cancel a volume upload whose write failed instead of leaving it open
Vedaant-J Oct 31, 2026
ea03c6d
Drop a trailing blank line flake8 flags in the volume tests
Vedaant-J Oct 31, 2026
03e9758
Merge #112's review fixes into #50
Vedaant-J Oct 31, 2026
9d33a31
Merge #112's review fixes into #60 through #50
Vedaant-J Oct 31, 2026
619fe19
Pass the write mode to the shared preflight for volume directories
Vedaant-J Oct 31, 2026
8d9e596
Merge main into #53
Vedaant-J Oct 31, 2026
eafb68c
Merge main and #53 into #112
Vedaant-J Oct 31, 2026
f6e4b3a
Merge #112 into #114
Vedaant-J Oct 31, 2026
009c6fa
Merge #112 into #50
Vedaant-J Oct 31, 2026
a519b99
Merge #50 into #60
Vedaant-J Oct 31, 2026
7b3fb84
Give an ADLS source's version its kind: exact for one object, listing…
Vedaant-J Oct 31, 2026
f648527
Merge #50's ADLS version kind into #60
Vedaant-J Oct 31, 2026
a3d88da
Ship chunked_fs in the public test image, which object_store_sink now…
Vedaant-J Oct 31, 2026
524ee75
Merge #112's public-image fix into #114
Vedaant-J Oct 31, 2026
3cfa550
Merge #112's public-image fix into #50
Vedaant-J Oct 31, 2026
dde9758
Merge #50's public-image fix into #60
Vedaant-J Oct 31, 2026
bd8fd5c
Import FitMode at module level in the engine trainer
Vedaant-J Oct 31, 2026
64d9dce
Share the refit suffix step in Trainer.get_log_dir
Vedaant-J Oct 31, 2026
bfb4497
Name the provenance file PROVENANCE_FILE everywhere instead of three …
Vedaant-J Oct 31, 2026
6afedc7
Read ingest stats inline where they are used
Vedaant-J Oct 31, 2026
39eae02
Warn once in report_other_graph with the detail that applies
Vedaant-J Oct 31, 2026
ae1717c
Share one artifact-root store warning across snapshots, tables and mo…
Vedaant-J Oct 31, 2026
95e3ce0
Drop the never-overridden _upload_file hook and its export-file indir…
Vedaant-J Oct 31, 2026
7281908
Pass fit provenance to _finish_fit as a FitRecord instead of a dict
Vedaant-J Oct 31, 2026
e48d607
Rename the shared training-table finish step to finish_generated and …
Vedaant-J Oct 31, 2026
709ea9c
Import finish_generated on one line
Vedaant-J Oct 31, 2026
7878a8d
Check manifest round trips once, through a model manifest with embedd…
Vedaant-J Oct 31, 2026
a02c647
Route volume model publish, fetch and preflight through FileStoreRouting
Vedaant-J Oct 31, 2026
85fdff0
Share one bounded put loop between a volume upload's writes and abort
Vedaant-J Oct 31, 2026
4685a33
Drop the fake volume's unused directory set, bad-request branch and s…
Vedaant-J Oct 31, 2026
1b7cb4e
Turn the Tracking protocol into a base class with defaults
Vedaant-J Oct 31, 2026
9fe3d75
Make MlflowTracking and SnowflakeTracking subclass Tracking
Vedaant-J Oct 31, 2026
e16a252
Call tracker members directly in MLOps and Trainer
Vedaant-J Oct 31, 2026
6706016
Score frames in the trainer predictor, which then needs no mlflow extra
Vedaant-J Oct 31, 2026
9f9ca7e
Return the alias error from _publish instead of appending it to a list
Vedaant-J Oct 31, 2026
2c20101
Read the encoders for the pip requirement with the trainer's loader
Vedaant-J Oct 31, 2026
5b865d0
Name the warning filter for what it hides and test it once
Vedaant-J Oct 31, 2026
d67ac28
Name the fit's start parameters and run summaries as steps of Trainer…
Vedaant-J Oct 31, 2026
151aa6c
Check publish and file-export support with the sink protocols, not ge…
Vedaant-J Oct 31, 2026
11b407b
Prune overlapping file-store tests
Vedaant-J Oct 31, 2026
fab1a11
Add FileStoreRouting to send a sink's file paths to FilesystemSink
Vedaant-J Oct 31, 2026
33b7f0d
Give the fake train-table requests a split_family, as the real one has
Vedaant-J Oct 31, 2026
f752cea
Test re-export over a missing file and a damaged export in one case
Vedaant-J Oct 31, 2026
819404e
Let file-holding sinks route their own paths; drop the engine's model…
Vedaant-J Oct 31, 2026
52487e6
Inline the single-use download_bundle and drop the unused bundle uplo…
Vedaant-J Oct 31, 2026
4143035
Route a file-holding sink's write_table and write_files through FileS…
Vedaant-J Oct 31, 2026
655c292
Merge vedaant/mlops-holdout-fit-mode into r10/112
Vedaant-J Oct 31, 2026
42a8dc6
Keep Tracking a protocol and put its defaults in TrackingBase
Vedaant-J Oct 31, 2026
5403e1d
Merge r10/112 into r10/114
Vedaant-J Oct 31, 2026
4320db4
Forward a foreign tracker's members and fall back to TrackingBase def…
Vedaant-J Oct 31, 2026
76acbea
Let FileStoreRouting send Databricks volume paths to file writers; ke…
Vedaant-J Oct 31, 2026
31252b6
Ask whether MLOps' tracker logs models in one helper
Vedaant-J Oct 31, 2026
2a4b2f7
Merge r10/112 into r10/50
Vedaant-J Oct 31, 2026
a3d4929
List the warnings fit and predict hide in one table
Vedaant-J Oct 31, 2026
dd8eeaf
Inline the MLflow target's registry URI and SageMaker session helpers
Vedaant-J Oct 31, 2026
af25464
Drop Dependencies' unused locations and read manifest dependencies di…
Vedaant-J Oct 31, 2026
b319b99
Render summaries once in TrackingBase for the MLflow and Snowflake tr…
Vedaant-J Oct 31, 2026
1ccd356
Resolve Snowflake's experiment schema once and drop checks on calls m…
Vedaant-J Oct 31, 2026
4b88e05
Inline MLflow's experiment creation and Delta source name and version…
Vedaant-J Oct 31, 2026
dcf56c9
Batch MLflow metrics and series through one helper
Vedaant-J Oct 31, 2026
43eb34c
Register an MLOps export in TrainingResult.export, sharing its publis…
Vedaant-J Oct 31, 2026
9d5851d
Set a published model's alias inside _publish
Vedaant-J Oct 31, 2026
d2191c1
Read the fetched model's metadata directly
Vedaant-J Oct 31, 2026
2915e90
Default _log_model's name and serving snapshot, and take the experime…
Vedaant-J Oct 31, 2026
d57699e
Inline the run records' text helper and simplify the tracking pointer
Vedaant-J Oct 31, 2026
d75d1f9
Shorten docstrings that repeat the code or docs/mlops.md
Vedaant-J Oct 31, 2026
6324879
Drop the Series, Curve, Histogram and Confusion summaries nothing pro…
Vedaant-J Oct 31, 2026
4627b81
Merge vedaant/mlops-snapshot-manifest into r10/60
Vedaant-J Oct 31, 2026
64eac88
Refuse a table path a file-only sink cannot write, with NotImplemente…
Vedaant-J Nov 1, 2026
0d3acd1
Merge #112's file-only sink error into #114
Vedaant-J Nov 1, 2026
2d044f2
Merge #112's file-only sink error into #50
Vedaant-J Nov 1, 2026
a66094b
Set MLflow's HTTP limits through the target's environ helper
Vedaant-J Nov 1, 2026
d879ab5
Read the alias of a Snowflake fetch location as a field
Vedaant-J Nov 1, 2026
fef887f
Drop the Snowflake hung-call test the tracking contract's hung-tracke…
Vedaant-J Nov 1, 2026
c919244
Merge #112's file-only sink error into #60 through #50
Vedaant-J Nov 1, 2026
bbad2a4
Build MLflow metric batches in _log_metrics
Vedaant-J Nov 1, 2026
5a3b430
Log log_model's metrics through MlflowTracking._log_metrics
Vedaant-J Nov 1, 2026
e4d390b
Trim docstrings, comments and messages in the registry and core packages
Vedaant-J Nov 1, 2026
e0907a2
Trim docstrings, comments and messages in the tracker integrations
Vedaant-J Nov 1, 2026
5de9a0b
Trim docstrings, comments and messages in kumo-engine MLOps
Vedaant-J Nov 1, 2026
6a8684b
Shorten the smoke check docstring and the mlflow test pin comment
Vedaant-J Nov 1, 2026
01d52b3
Cut the Databricks sink's class docstring to a summary and its argument
Vedaant-J Nov 1, 2026
c7588bf
Trim engine docstrings, comments and messages
Vedaant-J Nov 1, 2026
5746dee
Trim kumo-core and kumo-s3 docstrings and comments
Vedaant-J Nov 1, 2026
4bf0989
Trim test comments and match the shortened messages
Vedaant-J Nov 1, 2026
a3b3c31
Merge #50's text trim into #60
Vedaant-J Nov 1, 2026
3bd393a
Move _flatten next to its only caller in _run_spec
Vedaant-J Nov 1, 2026
1d2bb99
Drop fit_spec's tags parameter that only tests passed
Vedaant-J Nov 1, 2026
27c9a5b
Reuse bundle_manifest for a result's manifest or stub
Vedaant-J Nov 1, 2026
955e37b
Verify a fetched asset with loaded(), dropping a fallback no record r…
Vedaant-J Nov 1, 2026
192cdd7
Read GloVeVectors.path directly in TextEncoderAsset
Vedaant-J Nov 1, 2026
67651fb
Drop _new_run_name's unreachable None branch
Vedaant-J Nov 1, 2026
8d63cbc
Keep kumoapi's registry key prefix private
Vedaant-J Nov 1, 2026
a985faf
Drop the searcher test's identity check on the fallback sampler
Vedaant-J Nov 1, 2026
b81eb5c
Shorten the kumo-snowflake sdk extra comment
Vedaant-J Nov 1, 2026
3fbf44b
Keep the model download inside ObjectStoreSinkBackend._download_file
Vedaant-J Nov 1, 2026
80445ea
Import tempfile at module level in object_store_sink
Vedaant-J Nov 1, 2026
9136805
Type the file-sink helpers with FileStoreSink and Connector
Vedaant-J Nov 1, 2026
faad76d
Reuse the sink local in the file-store sink tests
Vedaant-J Nov 1, 2026
51a6df0
Merge main and #53 into #112
Vedaant-J Nov 1, 2026
a467ceb
Merge main into #114 through #112
Vedaant-J Nov 1, 2026
f6b4eb1
Merge main into #53
Vedaant-J Nov 1, 2026
1641553
Merge main into #50 through #112
Vedaant-J Nov 1, 2026
d35accf
Merge main into #60 through #50
Vedaant-J Nov 1, 2026
b2e9155
Merge origin/vedaant/mlops-mlflow-tracking into r11/60
Vedaant-J Nov 1, 2026
db42e55
Merge origin/vedaant/mlops-filestore-sink into r11/112
Vedaant-J Nov 1, 2026
dda58f5
Drop duplicate dockerignore entries the base already has
Vedaant-J Nov 1, 2026
c10be5a
Share one upload-finish path between write and close
Vedaant-J Nov 1, 2026
df21902
Share the fake volume sink setup across volume tests
Vedaant-J Nov 1, 2026
eb15c6d
Reflow the volume README paragraph
Vedaant-J Nov 1, 2026
6d4ee0e
Cut a warm-start parent to a stub with stub()
Vedaant-J Nov 1, 2026
0999522
Import the manifest helpers at the top of training_table
Vedaant-J Nov 1, 2026
c31373c
Copy the holdout into the bundle without returning the result unchanged
Vedaant-J Nov 1, 2026
3fe6442
Drop a stray blank line before ObjectStoreSinkBackend
Vedaant-J Nov 1, 2026
a742645
Read a version's kind only from a SourceVersion
Vedaant-J Nov 1, 2026
55316b2
Check a stub once, by equality, without a no-op embedded() case
Vedaant-J Nov 1, 2026
d25b9ac
Merge #112's re-review cleanups into #114
Vedaant-J Nov 1, 2026
681e481
Merge #112's re-review cleanups into #50
Vedaant-J Nov 1, 2026
3b48c3a
Merge #50's re-review cleanups into #60
Vedaant-J Nov 1, 2026
895fa00
Return a PublishedModel from the warm-start test's fake sink, as real…
Vedaant-J Nov 1, 2026
a3592c0
Log a registered result's source tables from its manifest's Datasets
Vedaant-J Nov 1, 2026
b47562e
Drop RunRecord.inputs: trackers get source tables as Dataset summaries
Vedaant-J Nov 1, 2026
9cf57c6
Keep a registered result's source-table logging best-effort
Vedaant-J Nov 1, 2026
da0f66f
Name the published model in the holdout test parameters
Vedaant-J Nov 1, 2026
9d82a62
Wrap the carry-forward fake training result like the formatter does
Vedaant-J Nov 1, 2026
204db0e
Merge #53 into #112
Vedaant-J Nov 1, 2026
b375ee5
Move metric_key into a shared _text module with the query's entity
Vedaant-J Nov 1, 2026
3e22e03
Refuse occupied output directories on every object store, by full loc…
Vedaant-J Nov 1, 2026
d5f46e4
Extract the model publisher check and the output preflight from fit a…
Vedaant-J Nov 1, 2026
9125de4
Merge #112 into #114
Vedaant-J Nov 1, 2026
d3985fe
Merge #112 into #50
Vedaant-J Nov 1, 2026
edbb28d
Name parent_model bundle_manifest, as it reads any bundle's manifest
Vedaant-J Nov 1, 2026
1f4f81f
Cut an embedded model's warm-start parent through a small helper
Vedaant-J Nov 1, 2026
2a4bf25
List the roles kept out of the artifact paths one per line
Vedaant-J Nov 1, 2026
d5997ca
Name runs the same on every tracker through _run_names
Vedaant-J Nov 1, 2026
1f5bb86
Store the model under the artifact root after the fit, from Trainer.fit
Vedaant-J Nov 1, 2026
b6fe35d
Report MLOps store failures as MLOps.on_error says
Vedaant-J Nov 1, 2026
689435f
Route summaries through _summary_tables instead of TrackingBase dispatch
Vedaant-J Nov 1, 2026
2e664c3
Publish a fitted bundle through a _publish helper
Vedaant-J Nov 1, 2026
c1d7bd0
Split Trainer._fit into warm-start, validation, materialization, trai…
Vedaant-J Nov 1, 2026
4c92ca4
Word the MLOps docstrings as the upper PRs do
Vedaant-J Nov 1, 2026
be6c8f3
Tag only models with their location when naming manifest artifacts
Vedaant-J Nov 1, 2026
10569d5
Name the changed columns in a schema change
Vedaant-J Nov 1, 2026
0180335
Lay out the MLOps doc in the upper PRs' order and words
Vedaant-J Nov 1, 2026
c0d7000
Check the recorded fit mode of each published model
Vedaant-J Nov 1, 2026
25b67dd
Log a fit's holdout as test/<metric> and register those metrics
Vedaant-J Nov 1, 2026
356fecf
Register a fit's logged model, or log one in no run, and name it afte…
Vedaant-J Nov 1, 2026
f76c349
Sample MLflow system metrics every second and log them in batches
Vedaant-J Nov 1, 2026
1ca5395
Hint once per server how to open SageMaker's MLflow UI, naming the run
Vedaant-J Nov 1, 2026
8bce1d4
Track trial runs only for searches, under the fit's snapshot and task…
Vedaant-J Nov 1, 2026
e6df805
Tag every run with the engine version and a fit with its entity
Vedaant-J Nov 1, 2026
5f3d13b
Summarize a fit's data as splits, health and column tables plus probl…
Vedaant-J Nov 1, 2026
fe1d471
Publish through TrainingResult.export in the filesystem-only sink test
Vedaant-J Nov 1, 2026
8c43cd8
Share the serving query and snapshot lookup in integrations/_serving
Vedaant-J Nov 1, 2026
61a68df
Read stored assets and snapshots with the identity the loading call w…
Vedaant-J Nov 1, 2026
88b8850
Build a Snowflake registry version's model arguments in one helper
Vedaant-J Nov 1, 2026
5063ded
Correct the MLOps guide for run names, registration, SageMaker and as…
Vedaant-J Nov 1, 2026
6ffa7ae
Merge #50's churn moves into #60
Vedaant-J Nov 1, 2026
4e0a992
Merge #114 into #50 to make the stack linear
Vedaant-J Nov 2, 2026
2cc6ece
Merge #114 through #50 into #60
Vedaant-J Nov 2, 2026
1476ea7
Skip summary types a tracker does not know instead of raising
Vedaant-J Nov 2, 2026
6633a4b
Merge main into #112
Vedaant-J Nov 2, 2026
4e22957
Merge main through #112 into #114
Vedaant-J Nov 2, 2026
8951887
Merge main through #114 into #50
Vedaant-J Nov 2, 2026
963a7e5
Merge main through #50 into #60
Vedaant-J Nov 2, 2026
3d3c9d8
Round-trip an artifact through an ADLS file destination
Vedaant-J Nov 2, 2026
3dfba07
Treat a chunked writer as failed until it opens, so a failed open nev…
Vedaant-J Nov 2, 2026
5f75e66
Drop only older committed generations, and read the newest one whose …
Vedaant-J Nov 2, 2026
746cedd
Merge #112's chunked-writer fixes and ADLS test into #114
Vedaant-J Nov 2, 2026
5ab71b1
Merge #112's chunked-writer fixes and ADLS test into #50
Vedaant-J Nov 2, 2026
923000e
Document that MLOps applies per call or block, not per model
Vedaant-J Nov 2, 2026
e6ca6f2
Merge #112's chunked-writer fixes and #50's MLOps scope note into #60
Vedaant-J Nov 2, 2026
2f73536
Describe the pyfunc as a platform adapter, not a serving or BP replac…
Vedaant-J Nov 2, 2026
f1c6773
Stamp artifact columns as Arrow constants instead of per-row Python l…
Vedaant-J Nov 2, 2026
8143a79
Document the pyfunc population param and its memory cost
Vedaant-J Nov 2, 2026
5fb2a8d
Reopen a volume download after a failed read
Vedaant-J Nov 2, 2026
a395236
Drop the pyfunc population param; Trainer.predict scores a population
Vedaant-J Nov 2, 2026
de29fc6
Merge #114's volume read retry fix into #50
Vedaant-J Nov 2, 2026
c921891
Merge #114's volume read retry fix into #60
Vedaant-J Nov 2, 2026
503f578
Stub the SageMaker experiment lookup with an experiment, so the test …
Vedaant-J Nov 2, 2026
64ea65c
Upload each stored training-table part as it is written
Vedaant-J Nov 2, 2026
ea5d22d
Pass an export's sidecars through a file-store sink's routing
Vedaant-J Nov 2, 2026
f074773
Merge #50's streamed training-table store into #60
Vedaant-J Nov 2, 2026
086c97f
Keep unsized volume exports as plain objects up to a part
Vedaant-J Nov 2, 2026
2bc3351
Document that volume exports need READ VOLUME too
Vedaant-J Nov 2, 2026
caef35f
Merge #114's plain volume exports through 4 GiB into #50
Vedaant-J Nov 2, 2026
c55e04a
Name a fit's datasets by what they are and label each with its role
Vedaant-J Nov 2, 2026
167384d
Split snapshot loading into allocate, verify and shared read helpers
Vedaant-J Nov 2, 2026
38091c2
Let an unusable MLflow server's own error say why, not blame credentials
Vedaant-J Nov 3, 2026
b7c7d6b
Share MLflow's metric batching between run and system metrics
Vedaant-J Nov 3, 2026
e754d3c
Give Snowflake's carrier-model arguments only the bundle they use
Vedaant-J Nov 3, 2026
dfa3f76
Drop run-name helpers nothing in this change calls
Vedaant-J Nov 3, 2026
f49809a
Return early from a publish that sets no alias
Vedaant-J Nov 3, 2026
7946f65
Say once in the docs that a held bundle reuses its registered version
Vedaant-J Nov 3, 2026
0527476
Inline the fit's duration and wrap its registration call
Vedaant-J Nov 3, 2026
2e54fec
Inline asset helpers that have one caller
Vedaant-J Nov 3, 2026
3f86367
Split a source table's change status into its current source and place
Vedaant-J Nov 3, 2026
c1f093e
Drop docstrings that restate private helpers' names
Vedaant-J Nov 3, 2026
b5c706d
Shorten the range-checksum doc comments
Vedaant-J Nov 3, 2026
d2d7e1a
Name the local and remote write checks of an artifact root
Vedaant-J Nov 3, 2026
6bea711
Read a training table export's name once when choosing sidecars
Vedaant-J Nov 3, 2026
24c1431
Share the label query and table generation across training table tests
Vedaant-J Nov 3, 2026
9a9057c
Export snapshots in tests through one helper
Vedaant-J Nov 3, 2026
6eac84e
Default the stub-fit helper's trainer and drop repeated model assertions
Vedaant-J Nov 3, 2026
7fdbf80
Score through one helper in the query-from-snapshot tests
Vedaant-J Nov 3, 2026
b571af1
Warn about an unreadable manifest from one handler
Vedaant-J Nov 3, 2026
8cbfdcb
Inline the fit-mode lookup into the holdout metrics
Vedaant-J Nov 3, 2026
e7067c6
Format Snowflake's schema-check error with full_error
Vedaant-J Nov 3, 2026
adad4f8
Trim volume and sink docstrings and redundant path stripping
Vedaant-J Nov 3, 2026
50ddd18
Tighten volume README and hoist test imports
Vedaant-J Nov 3, 2026
12faf65
Route FileStoreRouting's file paths through one helper
Vedaant-J Nov 3, 2026
bde829b
Raise the occupied-directory error from one place
Vedaant-J Nov 3, 2026
07b2caf
Drop docstrings that restate the file-store names
Vedaant-J Nov 3, 2026
48a8552
Share the streaming store fixture and the one-form check in the chunk…
Vedaant-J Nov 3, 2026
dafc543
Merge the two occupied-directory preflight tests
Vedaant-J Nov 3, 2026
6036fd2
Say in the docs that MLflow's default store is sqlite:///mlflow.db
Vedaant-J Nov 3, 2026
7fe756a
Say plainly when MLflow refuses a local folder store
Vedaant-J Nov 3, 2026
d78d12b
Delete manifest, warning and model helpers nothing in this PR uses
Vedaant-J Nov 3, 2026
0603c08
Merge #112's quality pass into #114
Vedaant-J Nov 3, 2026
f72ffa9
Merge #112's and #114's quality passes into #50
Vedaant-J Nov 3, 2026
d4b8ec4
Share the tracking URI, fit data and quick plan fixtures across track…
Vedaant-J Nov 3, 2026
4d8795a
Share the bundle and registry stubs in the Snowflake registry tests
Vedaant-J Nov 3, 2026
e19445a
Merge the Snowflake run-name tests and reuse FakeSession for schema c…
Vedaant-J Nov 3, 2026
b05fbbb
Share the vocab asset and volume stubs in the model-agnostic MLOps tests
Vedaant-J Nov 3, 2026
b3a1ddc
Fold the bundle-id pointer test into the publication test and share t…
Vedaant-J Nov 3, 2026
8dddaf9
Share one recording tracker and session start in the model-logging tests
Vedaant-J Nov 3, 2026
fccff2f
Test summary routing beside the summary table tests
Vedaant-J Nov 3, 2026
6f0974f
Record the Snowflake experiment on the fit's tracking ref
Vedaant-J Nov 3, 2026
c1fcd18
Merge #50's quality pass into #60
Vedaant-J Nov 3, 2026
09eef3a
Merge main into mlops-filestore-sink
Vedaant-J Nov 3, 2026
0c7d76c
Merge mlops-filestore-sink into mlops-dbx-volume
Vedaant-J Nov 3, 2026
a87135f
Merge mlops-dbx-volume into mlops-snapshot-manifest
Vedaant-J Nov 3, 2026
eb9d922
Check the trained clock before building the prediction table
Vedaant-J Nov 3, 2026
ccd4c40
Merge mlops-snapshot-manifest into mlops-mlflow-tracking
Vedaant-J Nov 3, 2026
fb03edd
Bound Databricks volume upload buffering
Vedaant-J Nov 3, 2026
2eb87dc
Merge mlops-dbx-volume into mlops-snapshot-manifest
Vedaant-J Nov 3, 2026
945ec4a
Merge mlops-snapshot-manifest into mlops-mlflow-tracking
Vedaant-J Nov 3, 2026
7460f8d
Merge origin/main into vedaant/mlops-snapshot-manifest
Vedaant-J Nov 3, 2026
bc4de08
Merge vedaant/mlops-snapshot-manifest into vedaant/mlops-mlflow-tracking
Vedaant-J Nov 3, 2026
ca72e13
Log a run's inputs in role order and show the snapshot's tables
Vedaant-J Nov 3, 2026
e4d98c0
Profile the snapshot input instead of listing its tables as columns
Vedaant-J Nov 3, 2026
320ffd1
Merge main into vedaant/mlops-mlflow-tracking
Vedaant-J Nov 3, 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
35 changes: 35 additions & 0 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,41 @@ jobs:
cd packages/kumo-sdk && python -m pytest -rs \
-W "ignore:You have an incompatible version of 'pyarrow' installed:UserWarning"

mlflow-floor:
# kumo-engine's `mlflow` extra allows the MLflow a Databricks Runtime
# ships; run the MLflow suites against the extra's floor. The tests left
# out read logs, which MLflow 3.5's SQLite migrations reconfigure.
needs: [diskgraph-wheel]
if: ${{ !cancelled() }}
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
ref: ${{ github.sha }}

- name: Set up packages
uses: ./.github/actions/setup
with:
github_token: ${{ secrets.KUMO_GITHUB_BOT_TOKEN }}
diskgraph-wheel-artifact: diskgraph-wheel

- name: Run the MLflow suites at the mlflow extra's floor
run: |
uv pip install --no-deps -e packages/kumo-engine
# mlflow 3.5.0 pins a pyarrow with no wheel for this Python; the
# extra installs mlflow-skinny, which holds the client and stores.
uv pip uninstall mlflow mlflow-skinny mlflow-tracing
uv pip install "mlflow-skinny==3.5.0"
python -c "import mlflow; assert mlflow.__version__ == '3.5.0'"
cd packages/kumo-engine && python -m pytest -rs \
test/test_mlflow_target.py test/test_mlflow_model.py \
test/test_mlflow_tracking.py -k "not (reuses_its_version or \
late_one_is_dropped or names_its_cause_and_can_raise or \
ends_runs_failed_and_is_not_raised)"

durations-aggregate:
runs-on: ubuntu-24.04
needs: [pytest]
Expand Down
118 changes: 118 additions & 0 deletions examples/mlops/smoke.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""Manual live check per platform; Databricks reads DATABRICKS_CLIENT_SECRET."""

import argparse
import os
import tempfile
import time
from pathlib import Path
from typing import Optional, Sequence

import numpy as np
import pandas as pd

import kumo.engine as kumo
from kumo.engine import ModelRegistryLocation
from kumo.engine.connector import (
DatabricksConnector,
DatabricksIdentity,
S3Identity,
SnowflakeConnector,
SnowflakeIdentity,
)

QUERY = 'PREDICT COUNT(orders.*, 0, 7, days) > 0 FOR EACH users.USER_ID'


def data(root: Path) -> kumo.Graph:
rng = np.random.default_rng(0)
root.mkdir(parents=True)
pd.DataFrame({'USER_ID': np.arange(200)}).to_parquet(root / 'users.parquet')
pd.DataFrame({
'ORDER_ID': np.arange(6000),
'USER_ID': rng.integers(0, 200, 6000),
'ORDER_TIME': pd.Timestamp('2024-01-01') +
pd.to_timedelta(rng.integers(0, 120, 6000), unit='D'),
}).to_parquet(root / 'orders.parquet')
files = kumo.FileConnector(root_dir=str(root))
orders = kumo.Table.from_source_table(
files['orders.parquet'], primary_key='ORDER_ID',
time_column='ORDER_TIME').infer_metadata()
return kumo.Graph(
tables={
'users': kumo.Table.from_source_table(
files['users.parquet'], primary_key='USER_ID'),
'orders': orders,
}, edges=[kumo.Edge(src_table='orders', fkey='USER_ID',
dst_table='users')])


def tracking(args: argparse.Namespace, suffix: str):
from kumo.engine.integrations.mlflow import MlflowTracking, SageMakerMLflow

if args.platform == 'databricks':
workspace = DatabricksConnector(
catalog=args.catalog, schema_name=args.schema,
warehouse_id=args.warehouse, identity=DatabricksIdentity(
host=args.host, client_id=args.client_id,
client_secret=os.environ['DATABRICKS_CLIENT_SECRET']))
return (MlflowTracking(target=workspace,
experiment_name=args.experiment),
f'{args.catalog}.{args.schema}.kumo_smoke_{suffix}')
if args.platform == 'sagemaker':
identity = S3Identity(role_arn=args.role_arn) if args.role_arn else None
return (MlflowTracking(
target=SageMakerMLflow(args.arn, identity=identity),
experiment_name='kumo-smoke'), f'kumo-smoke-{suffix}')
from kumo.engine.integrations.snowflake import SnowflakeTracking

connector = SnowflakeConnector(
warehouse=args.warehouse, database=args.database,
schema_name=args.schema, identity=SnowflakeIdentity(
account=args.account, username=args.user,
private_key=Path(args.private_key_file).read_text()))
return (SnowflakeTracking(connector, experiment_name='KUMO_SMOKE'),
f'KUMO_SMOKE_{suffix}')


def main(argv: Optional[Sequence[str]] = None) -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('platform',
choices=('databricks', 'sagemaker', 'snowflake'))
for flag in ('--host', '--client-id', '--catalog', '--schema',
'--warehouse', '--experiment', '--arn', '--role-arn',
'--database', '--account', '--user', '--private-key-file'):
parser.add_argument(flag)
parser.add_argument('--artifact-root',
help='defaults to a local temporary directory')
args = parser.parse_args(argv)

started = time.monotonic()
work = Path(tempfile.mkdtemp(prefix='kumo-smoke-'))
kumo.configure(scratch_dir=work / 'scratch')
graph = data(work / 'data')
tracker, model_name = tracking(args, time.strftime('%Y%m%d%H%M%S'))
location = ModelRegistryLocation(model_name, alias='candidate')
cfg = kumo.MLOps(args.artifact_root or str(work / 'root'),
tracking=tracker, registry=location, on_error='raise')
print(f'checked the target in {time.monotonic() - started:.0f} s')
with kumo.mlops(cfg):
snapshot = graph.snapshot()
query = kumo.PredictiveQuery(graph=graph, query=QUERY)
table = query.generate_training_table(snapshot)
plan = query.suggest_model_plan(snapshot)
plan.training_job.num_experiments = 1
plan.optimization.max_epochs = 1
result = kumo.Trainer(plan).fit(table, snapshot=snapshot)
print(f'fit run {result.tracking}, bundle {result.id}, '
f'registered {result.registered_model}')
loaded = kumo.Trainer.load_from_connector(cfg, location=location)
assert loaded.result.id == result.id, (loaded.result.id, result.id)
print('loaded the registered model back')
print(f'done in {time.monotonic() - started:.0f} s')


if __name__ == '__main__':
main()
58 changes: 55 additions & 3 deletions packages/kumo-api/kumoapi/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@
:class:`~kumoai_studio.backend.LocalBackend`.
"""

import re
from dataclasses import dataclass, field
from typing import (
Any,
Callable,
Dict,
List,
Mapping,
Optional,
Expand All @@ -42,10 +44,13 @@
from kumoapi.typing import Dtype

__all__ = [
'BUNDLE_ID_KEY',
'ENGINE_MARKER',
'ArtifactLocation',
'ArtifactSink',
'FileStoreSink',
'IngestResult',
'ModelAliasingSink',
'ModelFetchingSink',
'ModelListingSink',
'ModelPublishingSink',
Expand All @@ -56,8 +61,16 @@
'SourceBackend',
'SourceConnector',
'SourceCredentials',
'check_model_alias',
'registry_lineage',
]

_REGISTRY_KEY_PREFIX = 'kumo_'
ENGINE_MARKER = 'kumo_engine'
BUNDLE_ID_KEY = 'kumo_bundle_id'
_ALIAS = re.compile(r'[A-Za-z_][A-Za-z0-9_]{0,254}')
_RESERVED_ALIAS = re.compile(r'DEFAULT|FIRST|LAST|LATEST|V[0-9]+', re.I)


@dataclass(frozen=True)
class SourceCredentials:
Expand Down Expand Up @@ -149,13 +162,15 @@ class ModelRegistryLocation:
Args:
model_name: Registry-local model name.
version: Exact version to write or read. ``None`` lets the registry
assign the next one on export, and selects the latest on import.
An assigned version comes back on the export result.
assign the next one on export; on import it selects Snowflake's
default or Databricks' latest Kumo version.
comment: Optional description recorded on the version.
alias: Set on the new version on export; read on import.
"""
model_name: str
version: Optional[str] = None
comment: Optional[str] = None
alias: Optional[str] = None

def __post_init__(self) -> None:
if (not isinstance(self.model_name, str) or not self.model_name
Expand All @@ -166,6 +181,10 @@ def __post_init__(self) -> None:
raise ValueError(
'version must be a non-empty version, or None to let the '
'registry choose')
if self.alias is not None and (not self.alias
or self.version is not None):
raise ValueError('alias must be a non-empty alias, and is '
'mutually exclusive with version')


@dataclass(frozen=True)
Expand Down Expand Up @@ -702,7 +721,8 @@ def fetch_model(
local_dir: Directory to place the bundle in. Created if absent.
path: Source for a path-addressed target.
location: A registry location. A version of ``None`` selects the
latest, which is resolved here and reported back.
registry's choice (see :class:`ModelRegistryLocation`), which
is resolved here and reported back.
project_id: Authorizing project, where one applies.

Returns:
Expand All @@ -712,6 +732,38 @@ def fetch_model(
...


@runtime_checkable
class ModelAliasingSink(ArtifactSink, Protocol):
r"""A registry sink that sets and fetches by alias."""
def set_model_alias(
self,
connector: SourceConnector,
location: ModelRegistryLocation,
alias: str,
) -> None:
r"""Points :obj:`alias` at ``location.version``, moving it if held."""
...


def check_model_alias(alias: str) -> None:
r"""Raises :class:`ValueError` unless every registry accepts ``alias``."""
if (not isinstance(alias, str) or not _ALIAS.fullmatch(alias)
or _RESERVED_ALIAS.fullmatch(alias)):
raise ValueError(
f'{alias!r} cannot be a model alias: use letters, digits and '
f'underscores, not starting with a digit, other than the reserved '
f'DEFAULT, FIRST, LAST, LATEST and v<digits>.')


def registry_lineage(recorded: Mapping[str, Any]) -> Dict[str, str]:
r"""A version's ``kumo_*`` registry metadata, without the marker."""
return {
key: str(value)
for key, value in recorded.items()
if key.startswith(_REGISTRY_KEY_PREFIX) and key != ENGINE_MARKER
}


@runtime_checkable
class ModelListingSink(ArtifactSink, Protocol):
r"""A sink that can enumerate the models published through it.
Expand Down
33 changes: 33 additions & 0 deletions packages/kumo-api/test/test_plugin_registry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: Apache-2.0
import pytest

from kumoapi.plugin import check_model_alias, registry_lineage


@pytest.mark.parametrize('alias', [
'', ' ', 'default', 'LAST', 'Latest', 'v3', 'my-alias', '2nd', 'a' * 256,
None
])
def test_check_model_alias_refuses_what_a_registry_reserves(alias):
with pytest.raises(ValueError):
check_model_alias(alias)


def test_check_model_alias_accepts_a_plain_name():
check_model_alias('champion')
check_model_alias('_v2_prod')


def test_registry_lineage_keeps_kumo_keys_as_strings():
recorded = {
'kumo_engine': '1',
'kumo_bundle_id': 'b',
'auc': 0.9,
'kumo_seconds': 3
}
assert registry_lineage(recorded) == {
'kumo_bundle_id': 'b',
'kumo_seconds': '3'
}
10 changes: 7 additions & 3 deletions packages/kumo-core/src/kumo_core/artifact_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,10 +304,14 @@ def prepared_artifact_file(path, extra_columns=None, *, parquet_options=None,
writer = None
try:
for batch in local_parquet_batches(path):
frame = batch.to_pandas()
# Pandas turns all-null bool columns into Arrow nulls.
table = pa.Table.from_batches([batch])
for name, value in (extra_columns or {}).items():
frame[name] = value
table = pa.Table.from_pandas(frame, preserve_index=False)
column = pa.repeat(pa.scalar(value), table.num_rows)
table = (table.set_column(
table.column_names.index(name), name, column)
if name in table.column_names else
table.append_column(name, column))
if writer is None:
writer = pq.ParquetWriter(staged, table.schema,
**(parquet_options or {}))
Expand Down
32 changes: 32 additions & 0 deletions packages/kumo-core/src/kumo_core/artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from dataclasses import dataclass, field
from pathlib import Path
from typing import (
TYPE_CHECKING,
Any,
ClassVar,
Iterable,
Expand All @@ -26,6 +27,9 @@
from .io.json import load_json, save_json
from .manifest import RunManifest, RunPaths

if TYPE_CHECKING:
from .types import TaskType

PROVENANCE_FILE = '_provenance.json'


Expand Down Expand Up @@ -378,6 +382,26 @@ def add_bundle_file(bundle_dir: Path, role: str, relative_path: str) -> None:
_write_bundle_manifest(bundle_dir, manifest)


def remove_bundle_file(bundle_dir: Path, role: str) -> None:
"""Delete the file listed under ``role`` and its manifest entry."""
from .sources import layout

bundle_dir = Path(bundle_dir)
manifest_path = bundle_dir / VitalArtifactPublicationPolicy.MANIFEST_NAME
manifest = json.loads(manifest_path.read_text(encoding='utf-8'))
entries = manifest.get('artifacts') or []
removed = [entry for entry in entries if entry.get('role') == role]
if not removed:
return
manifest['artifacts'] = [
entry for entry in entries if entry.get('role') != role
]
_write_bundle_manifest(bundle_dir, manifest)
for entry in removed:
(bundle_dir / layout.confined(entry['relative_path'])).unlink(
missing_ok=True)


def _write_bundle_manifest(bundle_dir: Path,
manifest: Mapping[str, Any]) -> None:
payload = json.dumps(manifest, indent=2, sort_keys=True) + '\n'
Expand Down Expand Up @@ -475,6 +499,14 @@ def resolve_vital_artifact_sources(
}


def bundle_task_type(artifacts: Mapping[str, str]) -> TaskType:
"""The task type a bundle's prediction schema records."""
from .types import TaskType

schema = load_json(artifacts['prediction_schema'], dict)
return TaskType(schema['task_type'])


def stage_model_bundle(artifacts: Mapping[str, str], dest: Path) -> Path:
r"""Copies a trained run's artifacts into the layout scoring expects.

Expand Down
Loading
Loading