Skip to content

Add marimo MNIST + W&B Registry example#621

Open
johndmulhausen wants to merge 17 commits into
mainfrom
jmulhausen/marimo-mnist-registry-example
Open

Add marimo MNIST + W&B Registry example#621
johndmulhausen wants to merge 17 commits into
mainfrom
jmulhausen/marimo-mnist-registry-example

Conversation

@johndmulhausen

Copy link
Copy Markdown
Contributor

Summary

Adds the first marimo notebook to this repo at examples/marimo/mnist-registry/. The notebook trains a small PyTorch CNN on MNIST, logs the run to W&B, saves the trained weights as a model Artifact, and links the Artifact to a W&B Registry collection through the current cross-org run.link_artifact(target_path="wandb-registry-model/<collection>") API.

PEP 723 inline script metadata makes the notebook self-installing under uvx marimo edit mnist_registry.py --sandbox. No new top-level deps for the repo.

What the notebook demonstrates

  • Form-driven hyperparameters. mo.ui widgets bind to a config dict that feeds wandb.config.
  • Button-gated training. mo.ui.run_button plus mo.stop keeps slider tweaks from kicking off runs. After a run completes, clicking again starts a new run using the current form values; the previous run finishes cleanly first.
  • Defensive run lifecycle. wandb.finish() runs at the top of the training cell and at the end of the notebook so re-clicking Train in the same marimo kernel never nests runs.
  • Cross-org Registry linking. run.link_artifact(artifact=logged, target_path=f"wandb-registry-{registry}/{collection}") with a try/except that surfaces inline remediation guidance when the Registry is not provisioned, rather than crashing.
  • logged.wait() before linking prevents a race where link_artifact resolves an uncommitted version.
  • wandb.define_metric uses epoch as the x-axis for train/* and test/* series in the W&B UI.

Architecture and training loop mirror examples/pytorch/pytorch-cnn-mnist/main.py. Registry linking mirrors colabs/wandb_registry/zoo_wandb.ipynb.

Files

  • examples/marimo/mnist-registry/mnist_registry.py — the notebook (~640 lines)
  • examples/marimo/mnist-registry/README.md — prereqs, how to run, design notes
  • examples/marimo/mnist-registry/requirements.txt — pip mirror of the PEP 723 block

Commits

  1. add marimo MNIST + W&B Registry example — initial draft
  2. apply Google Developer Style Guide pass to marimo MNIST example — style cleanup (active voice, present tense, plain English, brand capitalization, code identifiers in backticks)

Test plan

  • python3 -c "import ast; ast.parse(open('examples/marimo/mnist-registry/mnist_registry.py').read())" succeeds (verified locally).
  • uvx marimo edit examples/marimo/mnist-registry/mnist_registry.py --sandbox opens the notebook with all cells rendering and no errors.
  • Hyperparameter form is interactive; nudging sliders does not start training before Train model is clicked.
  • Clicking Train model with defaults (3 epochs, batch 64, lr 0.01) starts a W&B run, completes in about two minutes on CPU, reaches >97% test accuracy.
  • An artifact mnist-cnn-<run-id> appears under the run's Artifacts tab with metadata for test accuracy, parameter count, and hyperparameters.
  • The Registry cell reports a successful link to wandb-registry-model/MNIST Classifiers; the version appears in the Model registry UI.
  • Changing a slider after the first run and clicking Train model again starts a new run cleanly (no wandb.init warnings about a pre-existing run).
  • Pointing the Registry name field at a non-existent registry surfaces a red mo.callout with remediation guidance and does not crash the notebook.
  • Unchecking Link artifact to Registry still produces a logged Artifact, with the Registry cell reporting that linking was skipped.

🤖 Generated with Claude Code

johndmulhausen and others added 2 commits May 13, 2026 12:16
Introduces the first marimo notebook in this repo. Trains a small PyTorch CNN
on MNIST, logs the run with wandb, saves the trained model as an Artifact,
and links it to a W&B Registry collection via the cross-org link_artifact
API.

The notebook is interactive: hyperparameters live in a marimo form and
training is gated by an explicit Train button so slider tweaks do not
trigger runs. PEP 723 inline script metadata makes it self-installing
under `uvx marimo edit --sandbox`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Active voice, present tense, plain English, and consistent brand
capitalization across the README and the notebook's prose cells.
Code identifiers wrapped in backticks where missing; a sentence
fragment in the verify step rewritten as a complete clause.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@socket-security

socket-security Bot commented May 13, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedpypi/​marimo@​0.23.972100100100100

View full report

@socket-security

socket-security Bot commented May 13, 2026

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
License policy violation: pypi marimo

Location: Package overview

From: examples/marimo/mnist-registry/requirements.txtpypi/marimo@0.23.9

ℹ Read more on: This package | This alert | What is a license policy violation?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Find a package that does not violate your license policy or adjust your policy to allow this package's license.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore pypi/marimo@0.23.9. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
License policy violation: pypi marimo

Location: Package overview

From: examples/marimo/mnist-registry/requirements.txtpypi/marimo@0.23.9

ℹ Read more on: This package | This alert | What is a license policy violation?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Find a package that does not violate your license policy or adjust your policy to allow this package's license.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore pypi/marimo@0.23.9. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

Mirrors the existing "Get Colabs" PR-comment flow (which serves .ipynb
files via nb_helpers) for marimo .py notebooks, which nb_helpers does not
handle. On each PR that touches a .py file, the job detects marimo notebooks
by their `marimo.App(` marker and upserts a single comment linking each to
molab — marimo's hosted runtime that loads any public notebook on GitHub.

Uses pull_request_target with pull-requests:write so the comment also lands
on fork PRs, and never checks out or runs PR code (reads file content as
text only). Links pin to the head commit SHA because branch names with
slashes make the molab blob/<ref>/<path> split ambiguous.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@johndmulhausen

johndmulhausen commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

▶️ Run the marimo notebook(s) in this PR

molab launches any public marimo notebook on GitHub in a hosted environment — no local setup required.

Notebook molab
examples/marimo/mnist-registry/mnist_registry.py Open in molab

Links track the head of jmulhausen/marimo-mnist-registry-example.

johndmulhausen and others added 2 commits June 12, 2026 13:08
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a masked mo.ui.text(kind="password") field so users can authenticate
by pasting a key instead of running `wandb login` in a shell. The resolved
value is kept out of the run config (never logged) and feeds
wandb.login(key=...) in the gated training cell; blank falls back to ambient
auth (shell login, WANDB_API_KEY, or netrc). Downstream cells read the key
via the returned wandb_api_key.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@johndmulhausen

johndmulhausen commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

Triage: Socket license alerts on marimo@0.23.9

Both alerts (sdist + wheel) are license-policy warnings (licenseSpdxDisj) — not vulnerability or malware findings — and the Socket checks themselves pass, so they're non-blocking.

marimo publishes under Apache-2.0, declared via the PEP 639 license_expression field with the legacy license field left null (verified on PyPI). Apache-2.0 is a permissive, OSI-approved license. The alert is an artifact of the license being expressed in the newer metadata format rather than a real licensing risk, so accepting it for this PR is appropriate.

@SocketSecurity ignore pypi/marimo@0.23.9

johndmulhausen and others added 6 commits June 12, 2026 13:26
The link step's "common causes" did not name the most common real failure:
a view-only org seat (`view-only member cannot write to project`), where the
run and artifact succeed but linking is blocked. Lead the remediation with
that case and how to resolve it, and note the write-access requirement up
front in the prerequisites.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GitHub resolves multi-segment (slashed) branch names in blob/<ref>/<path>
and molab fetches from GitHub, so a branch-based link points at the latest
revision and needs no per-commit comment maintenance. Content detection
still pins to the head SHA. Skip the comment write when the body is
unchanged so no-op pushes don't churn it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The view-only link failure now names the required role (Member) and the
three ways an admin can grant it: the Registry Members UI, the Python SDK
(wandb.Api().registry(...) then add_member()/update_member()), and SCIM
(PATCH /scim/Users/{id} with registryRoles), with a link to the configure
registry access docs. README prerequisite updated to match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The bare molab GitHub URL renders a static preview. Appending /server opens
the notebook in a hosted runtime so reviewers can actually run it — needed
here since the notebook depends on torch, which the in-browser /wasm mode
cannot run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a push-triggered workflow that runs `marimo export md` on each notebook
under examples/marimo/, writing a peer <notebook>.md, and commits the result
back to the branch so the rendered Markdown always tracks the notebook. marimo
is pinned for byte-deterministic output (no version-only churn), and three
guards prevent commit loops (GITHUB_TOKEN pushes don't re-trigger, *.py-only
trigger vs *.md-only commits, and [skip ci]).

Includes the initial export of mnist_registry.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collapses 19 cells into 5, breaking only where marimo requires it or where
the user actually provides input:

- intro (markdown)
- setup + form: imports, device detection, and all input widgets in one cell
- train button: its own cell (a widget must be defined separately from the
  cell that reads its value)
- run training: one gated cell that configs, authenticates, inits, builds and
  trains the model, logs, saves, and links the artifact — streaming each
  milestone with mo.output.append so it isn't a black box
- verify + next steps (markdown), rendered once a run exists

run_button.value resets to False after its cascade, so editing the form after
a run re-runs the training cell but mo.stop halts it immediately — no silent
retrain. Regenerates the Markdown peer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@johndmulhausen

Copy link
Copy Markdown
Contributor Author

Socket re-scan 8a2c0e64 — same marimo@0.23.9 license alert, re-affirming

This is the already-triaged licenseSpdxDisj warning, re-posted by a fresh scan on the latest push — not a new finding. marimo is Apache-2.0 (permissive, OSI-approved; verified on PyPI); the alert is about SPDX license-expression formatting, not a vulnerability or malware; and both Socket checks pass (non-blocking). No code change applies here — marimo is the notebook's runtime dependency and requirements.txt is a mirror of its PEP 723 block.

Re-affirming PR-scoped acceptance for this scan:

@SocketSecurity ignore pypi/marimo@0.23.9

Durable fix (so this stops re-nagging on every push): allowlist Apache-2.0 for the org in the Socket dashboard — an admin/security-team action, outside what I can do from a PR.

Copilot AI 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.

Pull request overview

Adds a new examples/marimo/ MNIST notebook demonstrating training a small PyTorch CNN, logging to W&B, and linking a logged model artifact into a W&B Registry collection, plus GitHub Actions automation for exporting marimo notebooks to Markdown and for posting molab “run this notebook” PR links.

Changes:

  • Add a self-contained marimo MNIST → W&B Artifact → W&B Registry notebook (+ exported Markdown peer) under examples/marimo/mnist-registry/.
  • Add docs and a requirements.txt mirror for the notebook’s PEP 723 dependency block.
  • Add GitHub Actions workflows to (a) auto-export marimo notebooks to Markdown on push and (b) comment molab links on PRs.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
examples/marimo/mnist-registry/mnist_registry.py New marimo notebook implementing training, W&B logging, artifact creation, and registry linking.
examples/marimo/mnist-registry/mnist_registry.md Exported Markdown version of the notebook for static rendering.
examples/marimo/mnist-registry/README.md Run instructions, prerequisites, and design notes for the example.
examples/marimo/mnist-registry/requirements.txt Pip requirements mirroring the PEP 723 dependency block.
.github/workflows/marimo_export_md.yml Workflow to export examples/marimo/**/*.py notebooks to peer .md files and commit updates.
.github/workflows/marimo_molab.yml Workflow to post/update PR comments with molab links for modified marimo notebooks.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/marimo_molab.yml Outdated
Comment thread examples/marimo/mnist-registry/mnist_registry.py Outdated
Comment thread examples/marimo/mnist-registry/mnist_registry.py
Comment thread examples/marimo/mnist-registry/mnist_registry.py Outdated
johndmulhausen and others added 6 commits June 12, 2026 15:32
- Remove the unused `import os` and drop `os` from the setup cell's returns.
- Compute final_acc from the full-precision last-epoch test_acc instead of the
  display-rounded history entry, so artifact `test_accuracy` keeps full precision.
- marimo_molab.yml: use `**/*.py` (canonical glob) instead of `**.py` so the
  path filter reliably matches notebooks in subdirectories.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reorders the notebook to: intro -> setup+form -> training pipeline -> Train
button -> verify. The button still gates the pipeline (so it doesn't run on
open), but now reads as the explicit "run the code above" trigger. The
training cell's pre-run message explains what it does and points to the button
below.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The entity form field read just "W&B entity", which reads as jargon. Label it
"username or team" to match the prerequisites wording. Also tighten the
registry-link remediation: an entity is a team or username (not an org), so it
now reads "set W&B entity to a team in an org where you have Registry write
access."

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Personal-username entities were removed for W&B accounts created after
2024-05-21, so a run's entity must be a team. Relabel the entity field
accordingly, fix the prerequisite, and wrap wandb.init so an
"entity ... not found" failure renders actionable guidance (set the entity to
a team) instead of a raw CommError traceback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pulls the CNN definition into its own cell so it can be shared, then adds a
cell that downloads the model from W&B (preferring the registered version,
falling back to the run's artifact so it works even when the registry link is
blocked), loads the weights into a fresh network, and classifies 10 held-out
MNIST test digits — rendering each as an image with predicted vs. true label
and an N/10 correct tally. No new dependency (numpy ships with torch; mo.image
renders the arrays).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The consume cell reused `correct` and `pred`, which the training cell already
defines — marimo forbids defining a name in more than one cell, so the cell
refused to run. Rename them to `n_correct` / `prediction` (both cell-local to
the consume step). marimo export does not enforce the single-definition rule,
only the kernel does, so this slipped through earlier validation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants