Skip to content

Conversation

@mscolnick
Copy link
Contributor

@mscolnick mscolnick commented Dec 16, 2025

Allows marimo to start with the kernel in a "lazy" state - users can view and edit cells before the kernel connects. The kernel only starts when explicitly triggered (clicking Run or Connect).

  • Faster initial load time - no waiting for kernel health checks
  • Better UX for reviewing/editing notebooks without needing to execute
  • Enables offline-first editing workflows

Flow

  1. App loads → Connection state = NOT_STARTED
  2. User edits cells locally
  3. User clicks Run/Connect → runtimeManager.init() → health check → WebSocket connects
  4. handleKernelReady sends edited codes to backend via sendInstantiate
  5. Backend executes with user's edited codes (respects auto_instantiate setting)

Key Changes

Frontend:

  • Added NOT_STARTED WebSocket state for pre-connection UI
  • createLazyRequests() wrapper intercepts all requests and triggers kernel init on first use
  • useConnectToRuntime() hook for manual connection trigger
  • NotStartedConnectionAlert component with "Click to connect" button
  • canInteractWithAppAtom enables UI interaction before connection
  • Cell edits made before connecting are sent to the backend

@vercel
Copy link

vercel bot commented Dec 16, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
marimo-docs Ready Ready Preview, Comment Jan 5, 2026 6:18pm

@mscolnick mscolnick changed the title [wip] lazy backend improvement: lazy backend Jan 5, 2026
@mscolnick mscolnick marked this pull request as ready for review January 5, 2026 18:17
@mscolnick mscolnick requested review from dmadisetti and removed request for Light2Dark and manzt January 5, 2026 18:17
@mscolnick mscolnick requested a review from Copilot January 5, 2026 19:10
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements lazy backend initialization for marimo, allowing users to view and edit notebooks before the kernel connects. The kernel now only starts when explicitly triggered by user actions like clicking Run or Connect, improving initial load times and enabling offline-first editing workflows.

Key Changes:

  • Introduced a new NOT_STARTED WebSocket state for pre-connection UI
  • Created createLazyRequests() wrapper to intercept requests and trigger kernel initialization on first use
  • Added useConnectToRuntime() hook and UI components for manual connection triggering
  • Modified runtime configuration to support lazy initialization with a new lazy boolean field

Reviewed changes

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

Show a summary per file
File Description
frontend/src/mount.tsx Updated Zod schemas to use looseObject() and added lazy flag to runtime config initialization
frontend/src/core/websocket/useMarimoKernelConnection.tsx Enabled lazy kernel support and removed TODO comment for existing cells handling
frontend/src/core/websocket/types.ts Added NOT_STARTED state to WebSocket state enum
frontend/src/core/websocket/connection-utils.ts Added isAppNotStarted() helper and NOT_STARTED connection tooltip
frontend/src/core/runtime/types.ts Added lazy boolean field to RuntimeConfig interface with documentation
frontend/src/core/runtime/runtime.ts Added debug logging for runtime initialization
frontend/src/core/runtime/config.ts Implemented useConnectToRuntime() hook for manual connection triggering, set lazy default to true
frontend/src/core/runtime/__tests__/runtime.test.ts Updated all test cases to include lazy: true in mock RuntimeConfig objects
frontend/src/core/runtime/__tests__/createWsUrl.test.ts Updated all test cases to include lazy: true in mock RuntimeConfig objects
frontend/src/core/network/resolve.ts Wrapped network requests with lazy initialization wrapper for non-WASM/non-static notebooks
frontend/src/core/network/requests-lazy.ts New file implementing lazy request interception logic with memoized initialization
frontend/src/core/network/requests-lazy.test.ts New comprehensive test suite for lazy requests functionality
frontend/src/core/network/connection.ts Changed default connection state to NOT_STARTED, added canInteractWithAppAtom and isNotStartedAtom
frontend/src/core/kernel/state.ts New file defining kernel instantiation state atom and wait helper
frontend/src/core/kernel/handlers.ts Updated to set kernel state after instantiation, added error logging for config parsing
frontend/src/core/islands/main.ts Added setKernelState NOOP to kernel ready handler options
frontend/src/core/edit-app.tsx Added NotStartedConnectionAlert component for empty notebooks
frontend/src/core/codemirror/cm.ts Removed dynamic readonly extension setup
frontend/src/components/editor/renderers/cell-array.tsx Replaced isConnectedAtom with canInteractWithAppAtom for button states, added NotStartedConnectionAlert
frontend/src/components/editor/notebook-cell.tsx Simplified cell action logic to remove connection checks for cell creation
frontend/src/components/editor/controls/Controls.tsx Replaced isConnectedAtom with canInteractWithAppAtom for run button state
frontend/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx Added click handler for connecting when not started, updated to use connection helper functions
frontend/src/components/editor/cell/code/cell-editor.tsx Replaced WebSocketState check with isAppConnecting() helper
frontend/src/components/editor/app-container.tsx Replaced WebSocketState check with isAppClosed() helper
frontend/src/components/editor/alerts/connecting-alert.tsx Refactored ConnectingAlert to use proper conditional rendering, added NotStartedConnectionAlert component

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

@@ -0,0 +1,65 @@
/* Copyright 2024 Marimo. All rights reserved. */
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

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

The copyright year is 2024, but other files in this PR use 2026. This should be updated to 2026 for consistency.

Suggested change
/* Copyright 2024 Marimo. All rights reserved. */
/* Copyright 2026 Marimo. All rights reserved. */

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,310 @@
/* Copyright 2024 Marimo. All rights reserved. */
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

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

The copyright year is 2024, but other files in this PR use 2026. This should be updated to 2026 for consistency.

Suggested change
/* Copyright 2024 Marimo. All rights reserved. */
/* Copyright 2026 Marimo. All rights reserved. */

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,16 @@
import { atom } from "jotai";
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

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

Missing copyright header. This file should include a copyright notice at the top, like "/* Copyright 2026 Marimo. All rights reserved. */".

Copilot uses AI. Check for mistakes.
return useEvent(async () => {
if (isAppNotStarted(connection.state)) {
setConnection({ state: WebSocketState.CONNECTING });
await runtimeManager.init();
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

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

The connection state is set to CONNECTING before calling runtimeManager.init(), but if init() fails or throws an error, the connection state remains stuck in CONNECTING. Consider wrapping this in a try-catch to handle initialization failures and set the connection state to CLOSED on error.

Suggested change
await runtimeManager.init();
try {
await runtimeManager.init();
} catch (error) {
Logger.log("Failed to initialize runtime", error);
setConnection({ state: WebSocketState.CLOSED });
throw error;
}

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

@dmadisetti dmadisetti left a comment

Choose a reason for hiding this comment

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

Couldn't get the preview to work, but the dynamic connection seems fine.

Only caveat was that after the 25 failed attempts, I needed to refresh (connect button didn't seem to work)

@@ -0,0 +1,310 @@
/* Copyright 2024 Marimo. All rights reserved. */
Copy link
Collaborator

Choose a reason for hiding this comment

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

frontend/src/core/network/__tests__/requests-lazy.test.ts ?

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.

3 participants