Skip to content

Record fits in experiment tracking and publish models to registries - #60

Open
Vedaant-J wants to merge 118 commits into
vedaant/mlops-snapshot-manifestfrom
vedaant/mlops-mlflow-tracking
Open

Vedaant-J wants to merge 118 commits into
vedaant/mlops-snapshot-manifestfrom
vedaant/mlops-mlflow-tracking

Conversation

@Vedaant-J

@Vedaant-J Vedaant-J commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Stack: #53 → #112 → #50 → #60 → #64; #114 on #112; #115 on #64. Merge bottom to top.

Summary

This PR adds experiment tracking and model registration for Trainer.fit. Before it, a fit reported progress only as progress-bar text on stdout and could not record itself anywhere a team already looks, and a chosen threshold had nowhere to live in the bundle.

  • kumo.MLOps(tracking=..., registry=...) records each fit as one run in MLflow (Databricks, SageMaker managed MLflow, any server) or Snowflake Experiment Tracking. The run gets params, per-split metrics, the search trials, and data summaries (split, health and column tables, notes, and the source tables as datasets) read from the stats files the builds already write. The model is then registered under an alias in that tracker's registry.
  • An MLflow-registered model is a pyfunc that Spark jobs and serving endpoints can score without the training code. kumo.engine.integrations.mlflow.log_model(trainer, snapshot=..., experiment_name=...) logs one outside a fit, creating the experiment if it is missing.
  • fit(callbacks=) and graph.snapshot(callbacks=) receive progress events. fit(scoring_defaults=) stores a threshold or top-k in the bundle.

Usage

import kumo.engine as kumo
from kumo.engine.integrations.mlflow import MlflowTracking

cfg = kumo.MLOps("s3://<bucket>/kumo", tracking=MlflowTracking("users-churn"),
                 registry=kumo.ModelRegistryLocation("users-churn", alias="candidate"),
                 on_error="raise")
with kumo.mlops(cfg):
    snapshot = graph.snapshot()
    result = kumo.Trainer(plan).fit(table, snapshot=snapshot,
                                    scoring_defaults=kumo.ScoringDefaults(binary_classification_threshold=0.5))
result.registered_model   # PublishedModel(name='users-churn', version='1', uri='models:/users-churn/1', ...)
champion = kumo.Trainer.load_from_connector(cfg, location=kumo.ModelRegistryLocation("users-churn", alias="champion"))

Snowflake uses SnowflakeTracking(connector, experiment_name=...). A custom tracker implements kumo.engine.tracking.Tracking.

Back-compat

Situation Behaviour
MLOps off, or kumo.MLOps without tracking and registry Unchanged.
A tracker raises, errors or hangs Tracking runs on a background thread on rank 0, so the fit is unaffected. on_error="warn" (default) warns once; "raise" raises after the result is stored.
Binary predict without a threshold Writes the probabilities and a null TARGET_PRED; before, it raised.
Existing bundles (no scoring-defaults.json, no dependencies) Load and predict as before, with the same bundle ids. An explicit argument always overrides a bundle default.
A bundle with scoring-defaults.json read by an older engine The file is listed and hash-checked, and the older engine ignores it.
Registry versions published before this PR Listed and fetched as before, with no kumo_bundle_id.
kumo-engine without the mlflow or snowflake extra import kumo.engine works. Building a tracker raises an ImportError naming the extra.

Testing

  • New engine tests: test_fit_events.py, test_tracking_contract.py, test_mlflow_tracking.py, test_mlflow_target.py, test_mlflow_model.py, test_snowflake_tracking.py, test_scoring_defaults.py, test_data_summaries.py, test_summary_tables.py, test_model_agnostic_mlops.py; test_trainer_registry.py grows with registration, alias and registry= cases.
  • The MLflow tests use a real local SQLite store and a real CPU fit, and check that the logged model scores the same as Trainer.predict. Snowflake tests use a recording fake of snowflake.ml.experiment whose signatures are checked against the installed library.
  • Package tests: the kumo-snowflake and kumo-databricks registry tests cover the bundle id, aliases and name checks. The kumo-core tests cover searched params, prediction columns, the worker-handle fallback and export column types.
  • CI adds an mlflow-floor job that runs the MLflow suites on mlflow-skinny 3.5.0.

Also fixes

  • pidfd fallback for sandboxes that refuse pidfd_open; encoder_selection install hint; Optuna ExperimentalWarning filter scoped to analyze().
  • kumo-core: stamping extra columns now stays in Arrow, so the all-null TARGET_PRED a threshold-less binary predict writes keeps its bool type.
  • kumo-core: searchers record the values each trial sampled, so tracked trial runs and trial_started events carry their searched params.

Change map: see the kumo-pr-change-map comment.

🤖 Generated with Claude Code

@Vedaant-J

Vedaant-J commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

Change map

File Function / scope What changed Why
packages/kumo-engine/kumo/engine/_mlops.py MLOps, snapshot_uri New tracking, registry; locate records stored locations; checks tracker and registry when built One switch turns on tracking and registration
packages/kumo-engine/kumo/engine/_events.py new: Event, EventEmitter Progress events with finite metrics; a raising callback is dropped Live fit and snapshot progress for callbacks and trackers
packages/kumo-engine/kumo/engine/tracking.py new public module Exports Event, Tracking, RunRecord API for writing a tracker
packages/kumo-engine/kumo/engine/_tracking.py new: RunSpec, RunRecord, Tracking, TrackingSession Run records; rank-0 background thread, 30 s drain, one-hour model wait Slow trackers never stall training
packages/kumo-engine/kumo/engine/trainer/_run_spec.py new: FitContext, fit_spec, fitted_spec Turns a fit into a RunSpec Keeps trackers independent of the trainer
packages/kumo-engine/kumo/engine/_data_summaries.py new: split_summaries, snapshot_summaries, table_details Split, health and column tables, notes and datasets from stats files Data facts on the run, no pass over data
packages/kumo-core/src/kumo_core/summaries.py new: Scalar, Tag, Series, Table, … Typed summary objects Summaries stay separate from tracker sinks
packages/kumo-engine/kumo/engine/_manifest.py TrackingRef, Dependency, AssetManifest, artifact_summaries Model manifest gains tracking, dependencies; locations and source tables become summaries Bundle points at its run and needs
packages/kumo-engine/kumo/engine/integrations/__init__.py new package Empty package marker Holds tracker integrations
packages/kumo-engine/kumo/engine/integrations/mlflow.py new: MlflowTracking, log_model, load_model MLflow runs, trial runs, metrics, tables, datasets, system metrics, registration Track and register fits in MLflow
packages/kumo-engine/kumo/engine/integrations/_mlflow_target.py new: Target, resolve, SageMakerMLflow Databricks, SageMaker and URI targets with per-call credentials Record anywhere from any host
packages/kumo-engine/kumo/engine/integrations/_mlflow_model.py new: KumoModel, predict, signature Pyfunc model with typed signature and per-call scratch Spark jobs and endpoints score registered models
packages/kumo-engine/kumo/engine/integrations/_mlflow_model_code.py new module set_model(KumoModel()) for models-from-code Pyfunc logged without pickle
packages/kumo-engine/kumo/engine/integrations/snowflake.py new: SnowflakeTracking Snowflake Experiment Tracking: one run per fit, trial prefixes, limits Track fits on Snowflake
packages/kumo-engine/kumo/engine/connector/snowflake.py SnowflakeConnector.session Returns the connector's Snowpark session Snowflake tracker reuses connector identity
packages/kumo-engine/kumo/engine/trainer/_scoring.py new: ScoringDefaults Validated scoring-defaults.json read and write Threshold travels with the model
packages/kumo-engine/kumo/engine/trainer/trainer.py fit, TrainingResult, _store_and_register, _publish callbacks=, scoring_defaults=; store, log, register; export, registered_model Fits tracked and registered in one order
packages/kumo-engine/kumo/engine/trainer/_spec.py SCORING_DEFAULTS_ROLE Accepts the scoring-defaults bundle role Bundles with defaults load
packages/kumo-engine/kumo/engine/trainer/_provenance.py FitRecord, embedded_model, record_derivation Manifest takes tracking, dependencies, derived_from Retuned bundles name their parent
packages/kumo-engine/kumo/engine/trainer/_runtime.py local_training_bindings call Passes instrumentation through kumo-core events reach the emitter
packages/kumo-engine/kumo/engine/trainer/output_config.py check_model_location Checks alias and model name before work Bad names fail fast
packages/kumo-engine/kumo/engine/trainer/__init__.py exports Exports ScoringDefaults Public name
packages/kumo-engine/kumo/engine/__init__.py exports Exports ScoringDefaults, TextEmbedding, WriteMode; imports _quiet Names users need at top level
packages/kumo-engine/kumo/engine/_deps.py new: Dependencies, FileAsset, MissingDependencyError Recorded dependencies; content-addressed assets under <root>/assets/ Serving finds what a bundle needs
packages/kumo-engine/kumo/engine/_predictor.py new: Predictor, TrainerPredictor, load_predictor Predictor entry point read from the bundle Serving not tied to Trainer
packages/kumo-engine/kumo/engine/_embeddings.py TextEncoderAsset, require_text_encoders Text encoders as stored assets Bundles never embed word vectors
packages/kumo-engine/kumo/engine/_scratch.py scoped_scratch_dir Per-context scratch root Each pyfunc call cleans up its files
packages/kumo-engine/kumo/engine/_quiet.py new module Imports PyG without torch's jit.script FutureWarning Warning users cannot act on
packages/kumo-engine/kumo/engine/_snapshot_io.py store Records the snapshot's stored location in its manifest Run points at stored artifacts
packages/kumo-engine/kumo/engine/graph.py Graph.snapshot, _SnapshotEvents callbacks=; snapshot events logged Snapshot progress visible
packages/kumo-engine/kumo/engine/pquery/training_table.py TrainingTable._store Records the table's stored location in its manifest Run points at stored artifacts
packages/kumo-core/src/kumo_core/instrumentation.py TrainingInstrumentation.event, format_error Additive no-op event hook Lets the engine receive trial events
packages/kumo-core/src/kumo_core/training/runtime.py local_training_bindings Accepts instrumentation Was hard-wired to no-op
packages/kumo-core/src/kumo_core/automl/auto_trainer.py AutoTrainer._emit, trial loop trial_started and trial_ended events with timing Trial progress for trackers
packages/kumo-core/src/kumo_core/automl/searcher.py Searcher.sampled_params Records the hyperparameters a search varied Trial params on runs
packages/kumo-core/src/kumo_core/automl/optuna_searcher.py OptunaSearcher.sample, analyze Fallback params kept; plot warnings ignored locally Correct params, quieter output
packages/kumo-core/src/kumo_core/callbacks/train_progress_logger.py AutoTrainingProgressLogger epoch_ended events with train_epoch_seconds Per-epoch metrics for trackers
packages/kumo-core/src/kumo_core/automl/runtime.py resolve_warm_start Uses bundle_task_type Shared helper
packages/kumo-core/src/kumo_core/artifacts.py remove_bundle_file, bundle_task_type Remove a listed bundle file; read task type Scoring defaults can be cleared
packages/kumo-core/src/kumo_core/prediction.py _score_prediction_shard Passes binary_labels Null labels without a threshold
packages/kumo-core/src/kumo_core/train/diskgraph_nested_trainer.py predict Null TARGET_PRED when no threshold Binary scoring without a threshold
packages/kumo-core/src/kumo_core/train/formatting.py prediction_output_columns Prediction columns per task type Typed pyfunc signature
packages/kumo-core/src/kumo_core/artifact_export.py prepared_artifact_file Extra columns stamped in Arrow All-null bool column keeps its type
packages/kumo-core/src/kumo_core/encoder_selection.py select_text_encoder Warning says how to install a word-vector model Actionable message
packages/kumo-core/src/kumo_core/diskgraph/lightning_data.py _WorkerHandle Falls back on ENOSYS/EPERM from pidfd_open Serving sandboxes refuse pidfds
packages/kumo-api/kumoapi/plugin.py ModelRegistryLocation.alias, ModelAliasingSink, check_model_alias Aliases, bundle-id key, registry lineage Promote by alias on every registry
packages/kumo-snowflake/src/kumo_snowflake/registry.py check_model_location, set_model_alias Bundle id, aliases, name checks, ML task Snowflake registry parity
packages/kumo-databricks/src/kumo_databricks/registry.py check_model_location, set_model_alias Bundle id tag, aliases, version reuse Unity Catalog parity, no duplicates
packages/kumo-databricks/src/kumo_databricks/session.py _client_for Pins auth_type='oauth-m2m' Ambient token cannot confuse auth
packages/kumo-engine/pyproject.toml extras New [mlflow] and [sagemaker] extras Opt-in installs
packages/kumo-snowflake/pyproject.toml sdk extra snowflake-ml-python>=2.0,<3 Experiment tracking API
setup.py test extra Adds full mlflow>=3.13,<4 Tests use MLflow's SQLite store
uv.lock lock file Locks the new dependencies Reproducible installs
.github/workflows/testing.yml new job mlflow-floor MLflow suites on mlflow-skinny 3.5.0 Guards the extra's floor
packages/kumo-engine/test/test_fit_events.py new tests Event order, failures, callbacks Event contract
packages/kumo-engine/test/test_tracking_contract.py new tests Fake fit replayed into both trackers Adapters match run_record
packages/kumo-engine/test/test_mlflow_tracking.py new tests Runs, series, datasets, failures, hung server MLflow tracker behaviour
packages/kumo-engine/test/test_mlflow_target.py new tests Credential scopes, checks, name rules Targets and credentials
packages/kumo-engine/test/test_mlflow_model.py new tests Log, register, serve, signature Pyfunc matches predict
packages/kumo-engine/test/test_snowflake_tracking.py new tests Run names, limits, instance in use Snowflake tracker behaviour
packages/kumo-engine/test/_snowflake_experiment_fake.py new test helper Recording fake of snowflake.ml.experiment Tests without an account
packages/kumo-engine/test/test_scoring_defaults.py new tests Validation, precedence, derived bundles Scoring defaults contract
packages/kumo-engine/test/test_trainer_registry.py registration tests Order, aliases, failures under both policies Registration contract
packages/kumo-engine/test/test_data_summaries.py new tests Summaries and warnings from stats files Data summaries
packages/kumo-engine/test/test_model_agnostic_mlops.py new tests Toy run, bundle, predictor, asset through seams Seams work beyond Trainer
packages/kumo-engine/test/test_artifacts.py store tests Locations recorded, ids kept; dependencies Run points at stored artifacts
packages/kumo-engine/test/test_mlops.py settings tests on_error values; registry name checks Fail at build time
packages/kumo-engine/test/test_snowflake_connector.py session test SnowflakeConnector.session() New method
packages/kumo-engine/test/test_optional_imports.py import test Trackers without extras raise naming the extra Extras stay optional
packages/kumo-engine/test/test_bundle_provenance.py fit helper Stores through _store_and_register Storing moved out of _finish_fit
packages/kumo-engine/test/test_embedding_models.py encoder warning tests Install hint; overridden columns not warned Quieter, actionable warnings
packages/kumo-engine/test/test_filestore_sink.py stage tests Publish through result.export; predict without a threshold Threshold now optional
packages/kumo-engine/test/test_prediction_completion.py trainer stub Stub carries scoring_defaults Predict reads bundle defaults
packages/kumo-engine/test/test_prediction_workers.py trainer stub Stub carries scoring_defaults Predict reads bundle defaults
packages/kumo-engine/test/test_semantic_carry_forward.py trainer stubs, threshold cases Stubs carry scoring_defaults; missing-threshold error case removed Binary threshold now optional
packages/kumo-core/tests/test_prediction_output_columns.py new tests Columns per task type Signature columns
packages/kumo-core/tests/test_searched_params.py new tests Only varied hyperparameters recorded Trial params
packages/kumo-core/tests/test_worker_handles.py new tests pidfd_open refusal falls back Serving sandboxes
packages/kumo-core/tests/test_optuna_searcher.py analyze test No experimental-plot warnings Quieter output
packages/kumo-core/tests/test_artifact_export.py export test All-null bool column stays bool Column types
packages/kumo-core/tests/test_artifacts.py bundle_task_type test Reads task type from the schema New helper
packages/kumo-core/tests/test_encoder_selection.py warning test Install hint in warning Message change
packages/kumo-api/test/test_plugin_registry.py alias tests Alias syntax and exclusivity New plugin API
packages/kumo-snowflake/tests/test_registry.py registry tests Bundle id, aliases, name checks Snowflake registry
packages/kumo-databricks/tests/test_registry.py registry tests Bundle id, aliases, reuse Unity Catalog registry
packages/kumo-databricks/tests/test_uc_registry.py tag assertion Marker tag checked among others Versions also carry the bundle id
packages/kumo-engine/docs/mlops.md quickstarts, trackers, registry, reference Platform quickstarts, promotion, pyfunc, credentials User docs
examples/mlops/smoke.py new script Manual live check per platform Verify a platform by hand
packages/kumo-engine/test/test_adls_connector.py test_invalid_prediction_output_fails_before_scoring Stub training result carries scoring_defaults Matches the result the trainer now reads
packages/kumo-engine/kumo/engine/_run_names.py new: run_name, fit_name, trial_name, record_name Run names built one way for MLflow and Snowflake Same run names on every tracker
packages/kumo-engine/kumo/engine/_summary_tables.py new: Routed, route, table_json Sorts summaries into metrics, tags, tables, card items and datasets; rounds table values One routing rule for every tracker
packages/kumo-engine/kumo/engine/_text.py new: entity, metric_key Shared text helpers Same metric names on every tracker
packages/kumo-engine/kumo/engine/integrations/_serving.py new: parsed_query, snapshot_location The query and snapshot lookup MLflow and Snowflake models share One lookup for both registries
packages/kumo-engine/test/test_summary_tables.py new Table rounding keeps definitions and counts exact Table contract
packages/kumo-engine/test/test_trainer_warnings.py warning filter test Torch-scatter, IterableDataset and fork warnings are hidden Covers the wider filter
packages/kumo-engine/test/test_warm_start.py warm-start test The fake sink returns a PublishedModel, as real sinks do Matches the sink contract

@copy-pr-bot

copy-pr-bot Bot commented Oct 20, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@Vedaant-J
Vedaant-J temporarily deployed to gha-kumo-workflow-tests October 20, 2026 22:13 — with GitHub Actions Inactive
@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review.

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: NVIDIA/kumo-engine/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Enterprise
  • Run ID: 69b8fedb-1ddc-416f-b70d-45bab0cef3e7
📥 Commits

Reviewing files that changed from the base of the PR and between 90ddb38 and 50dda4a.

⛔ Files ignored due to path filters: 1
  • uv.lock is excluded by !**/*.lock, !**/*.lock
📒 Files selected for processing: 8
  • packages/kumo-core/src/kumo_core/automl/auto_trainer.py
  • packages/kumo-engine/README.md
  • packages/kumo-engine/kumo/engine/_tracking.py
  • packages/kumo-engine/kumo/engine/integrations/mlflow.py
  • packages/kumo-engine/kumo/engine/tracking.py
  • packages/kumo-engine/kumo/engine/trainer/trainer.py
  • packages/kumo-engine/test/test_mlflow_tracking.py
  • packages/kumo-engine/test/test_tracking_contract.py

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 4 remain after this review.

Walkthrough

The change adds opt-in fit and trial tracking through a public tracking API and an MLflow integration. Trainer.fit starts and finishes tracking and forwards fit events. Trial logger callbacks pass through the training runtime to AutoTrainer, which supplies trial and attempt numbers. MLflow records fit and trial runs, metrics, parameters, and artifacts. The change also adds optional dependency handling, tests, and documentation.

Sequence Diagram(s)

sequenceDiagram
  participant Trainer.fit
  participant TrackingSession
  participant MlflowTracking
  participant AutoTrainer
  participant DiskgraphNestedTrainer
  Trainer.fit->>TrackingSession: start fit tracking
  TrackingSession->>MlflowTracking: create parent run
  AutoTrainer->>TrackingSession: request logger for trial and attempt
  TrackingSession->>MlflowTracking: create nested trial logger
  TrackingSession-->>AutoTrainer: return Lightning logger
  AutoTrainer->>DiskgraphNestedTrainer: pass Lightning logger
  Trainer.fit->>TrackingSession: forward events and finish
  TrackingSession->>MlflowTracking: record events and finish runs
Loading
Merge Risk: Low · previous review 50dda

Tracking is opt-in and its failures do not fail training. Some edge cases can still leave MLflow child runs marked RUNNING or disable tracking for a fit. These are bounded issues and worth fixing, so the change is mergeable with owner awareness.

Pre-merge checks: Passed 2 · Inconclusive 1

Failed Failed checks: 1 inconclusive

Check name Status Explanation Resolution
Change Map Design Review Inconclusive The required PR conversation comment beginning <!-- kumo-pr-change-map --> is not available. Repository search found only the template and review tooling, not the authored Change map. The supplied P… Provide the complete authored PR conversation comment beginning <!-- kumo-pr-change-map -->, including its Change map and supporting prose, or make the PR conversation available for inspection.
Passed Passed checks: 2 passed
Check name Status Explanation
Pr Size Passed The pull request changes 18 files. This does not exceed the failure threshold of 40 files.
Public Resource Access Passed All added package sources are publicly accessible. The PR adds the PyPI sources for mlflow and nvidia-ml-py in packages/kumo-engine/pyproject.toml:67-70 and uv.lock; the 86 new `files.pythonho…
Full details: Public Resource Access

Explanation

All added package sources are publicly accessible. The PR adds the PyPI sources for mlflow and nvidia-ml-py in packages/kumo-engine/pyproject.toml:67-70 and uv.lock; the 86 new files.pythonhosted.org artifact URLs tested successfully with HTTP 200. The relevant PyPI indexes also returned HTTP 200. No added repository URL was found, and the README references are illustrative documentation.

Full details: Change Map Design Review

Explanation

The required PR conversation comment beginning &lt;!-- kumo-pr-change-map --&gt; is not available. Repository search found only the template and review tooling, not the authored Change map. The supplied PR description is also explicitly truncated. The code diff is available, but it cannot establish whether the proposed map identified necessity, abstraction-layer placement, existing behavior, focused verification, and a stopping condition. This check therefore cannot assess the proposed design or its explanations.

Resolution

Provide the complete authored PR conversation comment beginning &lt;!-- kumo-pr-change-map --&gt;, including its Change map and supporting prose, or make the PR conversation available for inspection.

Finishing Touches
Fix failing CI checks
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

@Vedaant-J
Vedaant-J added this pull request to stack #61 October 20, 2026 22:15

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments: 2
packages/kumo-engine/kumo/engine/_tracking.py: 1

59-63: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not mark the session as started when context() raises.

start sets self._started = True before it builds the FitContext. The build happens inside the _call lambda, and it can raise. Two examples are a KeyError on train_table_spec['tt_id'] and a TypeError from to_json_dict. When the build raises, _call logs the error and Tracking.start is never called. The session still forwards later events, and Trainer.fit still calls finish. A third-party Tracking then gets on_event and finish without start, which breaks the lifecycle that the Tracking protocol documents. MlflowTracking handles this case only because _reset() clears its state.

Build the context first. Set _started only after the build succeeds. Keep _started = True before self._tracking.start(...), so that finish still runs cleanup when start itself raises.

Proposed fix
     def start(self, context: Callable[[], FitContext]) -> None:
         if self._tracking is None:
             return
-        self._started = True
-        self._call(lambda: self._tracking.start(context()))
+        fit_context = self._call(context)
+        if fit_context is None:
+            return
+        self._started = True
+        self._call(lambda: self._tracking.start(fit_context))
packages/kumo-core/src/kumo_core/train/trainer.py: 1

1443-1445: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Handle logger values that are lists or booleans.

create_pl_trainer wraps every supplied logger value in a list with the TensorBoard logger. A logger list becomes nested, and logger=False becomes a logger collection containing False. These values do not produce a valid Lightning logger collection. logger=False also no longer disables logging. The fix must preserve that boolean behavior.

Suggested fix
-from collections.abc import Callable
+from collections.abc import Callable, Iterable
...
         extra_logger = self.kwargs.pop("logger", None)
-        if extra_logger is not None:
+        if isinstance(extra_logger, bool):
+            pl_logger = extra_logger
+        elif isinstance(extra_logger, Iterable):
+            pl_logger = [pl_logger, *extra_logger]
+        elif extra_logger is not None:
             pl_logger = [pl_logger, extra_logger]

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: NVIDIA/kumo-engine/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Enterprise
  • Run ID: 7e4f692d-f9c2-4dc1-9c3a-15926fab9fd4
📥 Commits

Reviewing files that changed from the base of the PR and between d45e5ed and 74477db.

⛔ Files ignored due to path filters: 1
  • uv.lock is excluded by !**/*.lock, !**/*.lock
📒 Files selected for processing: 15
  • packages/kumo-core/src/kumo_core/automl/auto_trainer.py
  • packages/kumo-core/src/kumo_core/train/trainer.py
  • packages/kumo-core/src/kumo_core/training/orchestration.py
  • packages/kumo-core/src/kumo_core/training/runtime.py
  • packages/kumo-core/tests/test_training.py
  • packages/kumo-engine/kumo/engine/__init__.py
  • packages/kumo-engine/kumo/engine/_tracking.py
  • packages/kumo-engine/kumo/engine/integrations/__init__.py
  • packages/kumo-engine/kumo/engine/integrations/mlflow.py
  • packages/kumo-engine/kumo/engine/trainer/_runtime.py
  • packages/kumo-engine/kumo/engine/trainer/trainer.py
  • packages/kumo-engine/pyproject.toml
  • packages/kumo-engine/test/test_mlflow_tracking.py
  • packages/kumo-engine/test/test_optional_imports.py
  • setup.py

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 7 remain after this review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟡 Minor · Handle concurrent experiment creation without disabling tracking. · mlflow.py:153-158

packages/kumo-engine/kumo/engine/integrations/mlflow.py:153-158
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Handle concurrent experiment creation without disabling tracking.

When create_experiment raises RESOURCE_ALREADY_EXISTS, perform a fresh get_experiment_by_name lookup and use the returned experiment. Catch only this duplicate-creation error. Re-raise unrelated MLflow failures.

🛡️ Suggested fix
         if self.experiment_name is not None:
             experiment = self._client.get_experiment_by_name(
                 self.experiment_name)
-            experiment_id = (experiment.experiment_id
-                             if experiment else self._client.create_experiment(
-                                 self.experiment_name))
+            if experiment is None:
+                try:
+                    experiment_id = self._client.create_experiment(
+                        self.experiment_name)
+                except Exception as error:
+                    exceptions = import_provider(
+                        'mlflow.exceptions', 'mlflow')
+                    databricks_pb2 = import_provider(
+                        'mlflow.protos.databricks_pb2', 'mlflow')
+                    if not isinstance(error, exceptions.MlflowException) or (
+                            error.error_code !=
+                            databricks_pb2.RESOURCE_ALREADY_EXISTS):
+                        raise
+                    experiment = self._client.get_experiment_by_name(
+                        self.experiment_name)
+                    if experiment is None:
+                        raise
+                    experiment_id = experiment.experiment_id
+            else:
+                experiment_id = experiment.experiment_id
🧹 Nitpick comments: 1
packages/kumo-engine/test/test_tracking_contract.py: 1

378-388: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover global-rank gating in the tracking test.

The test covers only LOCAL_RANK=1. It does not detect a regression that removes RANK gating while retaining local-rank gating. Add the RANK=1, LOCAL_RANK=0 case.

🐛 Suggested fix
 def test_only_rank_zero_tracks(monkeypatch):
     monkeypatch.setenv('LOCAL_RANK', '1')
     recorder = _Recorder()
     session = TrackingSession(recorder)
 
     session.start(lambda: CONTEXT)
     session.on_event(EVENTS[0])
     session.finish(RESULT, None)
 
     assert not session.enabled
     assert recorder.calls == []
 
+def test_only_global_rank_zero_tracks(monkeypatch):
+    monkeypatch.setenv('RANK', '1')
+    monkeypatch.setenv('LOCAL_RANK', '0')
+    recorder = _Recorder()
+    session = TrackingSession(recorder)
+
+    session.start(lambda: CONTEXT)
+    session.on_event(EVENTS[0])
+    session.finish(RESULT, None)
+
+    assert not session.enabled
+    assert recorder.calls == []

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: NVIDIA/kumo-engine/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Enterprise
  • Run ID: 413901b2-1304-4d92-8208-5f67666de42c
📥 Commits

Reviewing files that changed from the base of the PR and between c4c4d43 and 5e0624d.

📒 Files selected for processing: 10
  • packages/kumo-core/src/kumo_core/automl/auto_trainer.py
  • packages/kumo-core/src/kumo_core/train/trainer.py
  • packages/kumo-core/tests/test_training.py
  • packages/kumo-engine/README.md
  • packages/kumo-engine/kumo/engine/__init__.py
  • packages/kumo-engine/kumo/engine/_tracking.py
  • packages/kumo-engine/kumo/engine/integrations/mlflow.py
  • packages/kumo-engine/kumo/engine/trainer/trainer.py
  • packages/kumo-engine/test/test_mlflow_tracking.py
  • packages/kumo-engine/test/test_tracking_contract.py

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 8 remain after this review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: NVIDIA/kumo-engine/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Enterprise
  • Run ID: 4675a596-3919-4607-940f-c95403683072
📥 Commits

Reviewing files that changed from the base of the PR and between 5e0624d and 90ddb38.

📒 Files selected for processing: 5
  • packages/kumo-engine/README.md
  • packages/kumo-engine/kumo/engine/_tracking.py
  • packages/kumo-engine/kumo/engine/integrations/mlflow.py
  • packages/kumo-engine/test/test_mlflow_tracking.py
  • packages/kumo-engine/test/test_tracking_contract.py

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 11 remain after this review.

@Vedaant-J
Vedaant-J force-pushed the vedaant/mlops-mlflow-tracking branch from 90ddb38 to 50dda4a Compare October 21, 2026 08:47
@Vedaant-J
Vedaant-J temporarily deployed to gha-kumo-workflow-tests October 21, 2026 08:47 — with GitHub Actions Inactive
@Vedaant-J Vedaant-J mentioned this pull request Oct 21, 2026
2 tasks done
@Vedaant-J
Vedaant-J force-pushed the vedaant/mlops-mlflow-tracking branch from 50dda4a to 11f23bd Compare October 21, 2026 09:58
@Vedaant-J
Vedaant-J temporarily deployed to gha-kumo-workflow-tests October 21, 2026 09:58 — with GitHub Actions Inactive
Vedaant-J added a commit that referenced this pull request Oct 21, 2026
Squashed copy of #60 (vedaant/mlops-mlflow-tracking) at its current
head, so this branch builds until #60 merges. Review #60 itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Vedaant-J
Vedaant-J force-pushed the vedaant/mlops-mlflow-tracking branch from 11f23bd to ec7ea6e Compare October 21, 2026 15:57
@Vedaant-J
Vedaant-J temporarily deployed to gha-kumo-workflow-tests October 21, 2026 15:57 — with GitHub Actions Inactive
Vedaant-J added a commit that referenced this pull request Oct 21, 2026
Squashed copy of #60 (vedaant/mlops-mlflow-tracking) at its current
head, so this branch builds until #60 merges. Review #60 itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Vedaant-J added a commit that referenced this pull request Oct 21, 2026
Squashed copy of vedaant/mlops-fit-events..vedaant/mlops-mlflow-tracking
(ec7ea6e) so prediction tracking builds on the fit tracking
stack. Review it in #60; drop this commit when #60 merges. Applied
without conflicts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Vedaant-J
Vedaant-J force-pushed the vedaant/mlops-mlflow-tracking branch from ec7ea6e to be04f00 Compare October 21, 2026 19:08
Vedaant-J and others added 30 commits November 1, 2026 01:31
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eaches

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… sinks do

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fits are fit-<model>-<start>, trials trial-<n>-<fit>, a user run_name
names the fit fit-<name>, and Snowflake numbers a taken name _2, _3.
Trackers report the experiment and run name on TrackingRef.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds Note, Table.brief and Scalar.role; MLflow logs a run's tables with one
loggedArtifacts update and digests datasets by name, source and schema.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Trackers and registries get the shipped model's holdout (before_refit/ for
the pre-refit one); fits drop seconds and the alias tag. Snowflake keeps
each trial's best tune metric under TRIAL_<n>/ plus a run's
duration_seconds, MLflow times runs from the record, and a Snowflake
registry version defaults to the next V<n> and records the holdout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r the model

register() reuses the model the fit run logged, else logs it with no run in
the configured or model-named experiment; load_model resolves an alias to
its version first. Callers look up the held version once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SageMaker scopes also stop MLflow printing run links, and the registry
version error names the tracker rather than MLflow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… type

A single-trial fit logs its epochs on the fit run alone, and searches no
longer log a search/best_* series.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fit params skip unset plan values, and the tune metric uses the logged
metric name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…em notes

Data warnings become one Note per problem with a count tag; a manifest's
artifacts are a locations table, its datasets are named by kind and date,
and the snapshot and training table get schema and size details.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…as given

A model whose assets sit on a volume, stage or bucket now loads outside a
kumo.mlops block through load_model's target, load_from_connector's
connector or the pyfunc's model_config['assets_from']; when none can read
them, the error names the argument to pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…set loading

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
gha-kumo-workflow-tests — 6ffa7ae5 Deployed Nov 1, 2026 by Vedaant-J via import #1165
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants