From 31a9831970793a123e7120d5461291b03757b77f Mon Sep 17 00:00:00 2001 From: Vishal Date: Wed, 1 Apr 2026 21:49:21 +0530 Subject: [PATCH 1/8] A feat: add configurable default base image for steps (settings > env > default) Signed-off-by: Vishal --- kale/compiler.py | 4 ++-- kale/pipeline.py | 22 ++++++++++++---------- kale/processors/nbprocessor.py | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/kale/compiler.py b/kale/compiler.py index 8da3e7aa2..8eebf81cc 100644 --- a/kale/compiler.py +++ b/kale/compiler.py @@ -24,7 +24,7 @@ from kale import __version__ as KALE_VERSION from kale.common import graphutils, kfputils, utils from kale.common.imports import get_packages_to_install -from kale.pipeline import DEFAULT_BASE_IMAGE, Pipeline, PipelineParam, Step +from kale.pipeline import Pipeline, PipelineParam, Step log = logging.getLogger(__name__) @@ -204,7 +204,7 @@ def _encode_source(s): step_inputs=step_inputs, step_outputs=step_outputs, kfp_dsl_artifact_imports=KFP_DSL_ARTIFACT_IMPORTS, - default_base_image=DEFAULT_BASE_IMAGE, + default_base_image=self.pipeline.config.base_image, **self.pipeline.config.to_dict(), ) return autopep8.fix_code(fn_code) diff --git a/kale/pipeline.py b/kale/pipeline.py index a327b9650..2984ce9bb 100644 --- a/kale/pipeline.py +++ b/kale/pipeline.py @@ -134,16 +134,18 @@ def _randomize_pipeline_name(self): self.pipeline_name = f"{self.pipeline_name}-{utils.random_string()}" def _set_base_image(self): - if not self.base_image: - try: - self.base_image = podutils.get_docker_base_image() - except (ConfigException, RuntimeError, FileNotFoundError, ApiException): - # * ConfigException: no K8s config found - # * RuntimeError, FileNotFoundError: this is not running in a - # pod - # * ApiException: K8s call to read pod raised exception; - # Use kfp default image - self.base_image = DEFAULT_BASE_IMAGE + # Priority 1: Settings (already in self.base_image) + if self.base_image: + return + + # Priority 2: Environment variable + env_image = os.getenv("DEFAULT_BASE_IMAGE") + if env_image: + self.base_image = env_image + return + + # Priority 3: Default + self.base_image = DEFAULT_BASE_IMAGE def _set_volume_storage_class(self): if not self.storage_class_name: diff --git a/kale/processors/nbprocessor.py b/kale/processors/nbprocessor.py index e530adba1..d28868ce0 100644 --- a/kale/processors/nbprocessor.py +++ b/kale/processors/nbprocessor.py @@ -376,7 +376,7 @@ def parse_notebook(self): limits=tags.get("limits", {}), labels=tags.get("labels", {}), annotations=tags.get("annotations", {}), - base_image=tags.get("base_image", ""), + base_image=tags.get("base_image") or self.pipeline.config.base_image, enable_caching=tags.get("enable_caching"), ) self.pipeline.add_step(step) From c2cfabb1bfcecb6fcff502664107358afd6e4dae Mon Sep 17 00:00:00 2001 From: Vishal Date: Wed, 1 Apr 2026 22:03:07 +0530 Subject: [PATCH 2/8] fix: remove unused kubernetes imports Signed-off-by: Vishal --- kale/pipeline.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/kale/pipeline.py b/kale/pipeline.py index 2984ce9bb..2fe9adcb4 100644 --- a/kale/pipeline.py +++ b/kale/pipeline.py @@ -17,8 +17,6 @@ import logging import os -from kubernetes.client.rest import ApiException -from kubernetes.config import ConfigException import networkx as nx from kale.common import graphutils, podutils, utils From 02431db99a9012d19d5f63257ab095db462c62c3 Mon Sep 17 00:00:00 2001 From: Vishal Date: Wed, 1 Apr 2026 23:30:10 +0530 Subject: [PATCH 3/8] feat: add UI support for configurable base image (system & pipeline defaults) Signed-off-by: Vishal --- .../widgets/cell-metadata/CellMetadataEditor.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/labextension/src/widgets/cell-metadata/CellMetadataEditor.tsx b/labextension/src/widgets/cell-metadata/CellMetadataEditor.tsx index 235a84fa5..3f4666137 100644 --- a/labextension/src/widgets/cell-metadata/CellMetadataEditor.tsx +++ b/labextension/src/widgets/cell-metadata/CellMetadataEditor.tsx @@ -663,21 +663,25 @@ export class CellMetadataEditor extends React.Component { Base Image for Step

- Default:{' '} + System Default:{' '} {this.props.defaultBaseImage || DEFAULT_BASE_IMAGE}

+ +

+ Pipeline Default:{' '} + + {this.props.pipelineBaseImage || + 'Not set (uses system default)'} + +

this.updateBaseImage(v)} - placeholder={ - this.props.pipelineBaseImage || - this.props.defaultBaseImage || - DEFAULT_BASE_IMAGE - } + placeholder="e.g. python:3.12" style={{ width: '100%', marginTop: '8px' }} />
From 10de22cd1aeb711b7a37348a4c3923642edf08f9 Mon Sep 17 00:00:00 2001 From: Vishal Date: Mon, 6 Apr 2026 22:36:49 +0530 Subject: [PATCH 4/8] feat: add JupyterLab settings support for default base image with live UI updates Signed-off-by: Vishal --- labextension/src/widget.tsx | 53 ++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/labextension/src/widget.tsx b/labextension/src/widget.tsx index 9e6ac3ec9..11d298d08 100644 --- a/labextension/src/widget.tsx +++ b/labextension/src/widget.tsx @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - +import { ISettingRegistry } from '@jupyterlab/settingregistry'; import { JupyterFrontEnd, JupyterFrontEndPlugin, @@ -62,7 +62,13 @@ let kalePanelWidget: ReactWidget | undefined; export default { activate, id, - requires: [ILabShell, ILayoutRestorer, INotebookTracker, IDocumentManager], + requires: [ + ILabShell, + ILayoutRestorer, + INotebookTracker, + IDocumentManager, + ISettingRegistry + ], provides: IKubeflowKale, autoStart: true, } as JupyterFrontEndPlugin; @@ -73,6 +79,7 @@ async function activate( restorer: ILayoutRestorer, tracker: INotebookTracker, docManager: IDocumentManager, + settingRegistry: ISettingRegistry, ): Promise { const kernel: Kernel.IKernelConnection = await NotebookUtils.createNewKernel(); @@ -96,6 +103,31 @@ async function activate( // TODO: backend can become an Enum that indicates the type of // env we are in (like Local Laptop, MiniKF, GCP, UI without Kale, ...) const backend = await getBackend(kernel); + const settings = await settingRegistry.load('jupyterlab-kubeflow-kale:deploymentPanel'); + + let defaultBaseImage = + (settings.get('default_base_image').composite as string) || + 'python:3.12'; + + settings.changed.connect(() => { + defaultBaseImage = + (settings.get('default_base_image').composite as string) || + 'python:3.12'; + + if (kalePanelWidget) { + const newWidget = createPanel(defaultBaseImage); + + newWidget.id = KALE_PANEL_ID; + newWidget.title.icon = kaleIcon; + newWidget.title.caption = 'Kubeflow Pipelines Deployment Panel'; + newWidget.node.classList.add('kale-panel'); + + labShell.add(newWidget, 'left'); + + kalePanelWidget = newWidget; + } + }); + if (backend) { try { await executeRpc(kernel, 'log.setup_logging'); @@ -130,12 +162,8 @@ async function activate( kalePanelWidget.activate(); } } - - // Creates the left side bar widget once the app has fully started - lab.started.then(() => { - // show list of commands in the commandRegistry - // console.log(lab.commands.listCommands()); - kalePanelWidget = ReactWidget.create( + function createPanel(defaultBaseImage: string) { + return ReactWidget.create( setLeftPanelRef(ref)} lab={lab} @@ -143,8 +171,15 @@ async function activate( docManager={docManager} backend={backend} kernel={kernel} - />, + defaultBaseImage={defaultBaseImage} + /> ); + } + // Creates the left side bar widget once the app has fully started + lab.started.then(() => { + // show list of commands in the commandRegistry + // console.log(lab.commands.listCommands()); + kalePanelWidget = createPanel(defaultBaseImage); kalePanelWidget.id = KALE_PANEL_ID; kalePanelWidget!.title.icon = kaleIcon; kalePanelWidget!.title.caption = 'Kubeflow Pipelines Deployment Panel'; From 5cb4eca8ac1ba79123f6fbe19b692f654775b7ff Mon Sep 17 00:00:00 2001 From: Vishal Date: Fri, 10 Apr 2026 12:06:59 +0530 Subject: [PATCH 5/8] fix: resolve client-side base image handling and remove hardcoded defaults Signed-off-by: Vishal --- labextension/src/widget.tsx | 73 ++------------------------ labextension/src/widgets/LeftPanel.tsx | 11 ++++ 2 files changed, 14 insertions(+), 70 deletions(-) diff --git a/labextension/src/widget.tsx b/labextension/src/widget.tsx index 5b44074b7..c81423409 100644 --- a/labextension/src/widget.tsx +++ b/labextension/src/widget.tsx @@ -11,7 +11,6 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -import { ISettingRegistry } from '@jupyterlab/settingregistry'; import { JupyterFrontEnd, JupyterFrontEndPlugin, @@ -55,10 +54,6 @@ export const KALE_PANEL_ID = 'jupyterlab-kubeflow-kale/kubeflowDeployment'; const id = 'jupyterlab-kubeflow-kale:deploymentPanel'; -const KALE_SETTINGS_PLUGIN_ID = 'jupyterlab-kubeflow-kale:kale-settings'; -const ENABLE_KALE_BY_DEFAULT_KEY = 'enableKaleByDefault'; -const AUTO_SAVE_ON_COMPILE_OR_RUN_KEY = 'autoSaveOnCompileOrRun'; - const kaleIcon = new LabIcon({ name: 'kale:logo', svgstr: kaleIconSvg }); let kalePanelWidget: ReactWidget | undefined; @@ -118,7 +113,7 @@ async function activate( settings.changed.connect(() => { defaultBaseImage = (settings.get('default_base_image').composite as string) || - 'python:3.12'; + ''; if (kalePanelWidget) { const newWidget = createPanel(defaultBaseImage); @@ -143,70 +138,6 @@ async function activate( } } - // Load and react to Kale JupyterLab settings - const SettingsAwareLeftPanel = () => { - const [kaleSettings, setKaleSettings] = React.useState({ - enableKaleByDefault: false, - autoSaveOnCompileOrRun: false, - }); - - React.useEffect(() => { - let disposed = false; - let setting: any | null = null; - let onSettingChanged: (() => void) | null = null; - - settingRegistry - .load(KALE_SETTINGS_PLUGIN_ID) - .then(loadedSetting => { - setting = loadedSetting; - - const read = () => ({ - enableKaleByDefault: - (loadedSetting.get(ENABLE_KALE_BY_DEFAULT_KEY).composite as - | boolean - | undefined) ?? false, - autoSaveOnCompileOrRun: - (loadedSetting.get(AUTO_SAVE_ON_COMPILE_OR_RUN_KEY).composite as - | boolean - | undefined) ?? false, - }); - - const update = () => { - if (disposed) { - return; - } - setKaleSettings(read()); - }; - - update(); - onSettingChanged = () => update(); - (loadedSetting.changed as any).connect(onSettingChanged); - }) - .catch(reason => { - console.error('Failed to load Kale settings:', reason); - }); - - return () => { - disposed = true; - if (setting && onSettingChanged) { - (setting.changed as any).disconnect(onSettingChanged); - } - }; - }, []); - - return ( - setLeftPanelRef(ref)} - lab={lab} - tracker={tracker} - docManager={docManager} - backend={backend} - kernel={kernel} - enableKaleByDefault={kaleSettings.enableKaleByDefault} - autoSaveOnCompileOrRun={kaleSettings.autoSaveOnCompileOrRun} - /> - ); - }; async function loadPanel() { let reveal_widget = undefined; @@ -242,6 +173,8 @@ async function activate( docManager={docManager} backend={backend} kernel={kernel} + enableKaleByDefault={false} + autoSaveOnCompileOrRun={false} defaultBaseImage={defaultBaseImage} /> ); diff --git a/labextension/src/widgets/LeftPanel.tsx b/labextension/src/widgets/LeftPanel.tsx index a91bb9b02..153759dfb 100644 --- a/labextension/src/widgets/LeftPanel.tsx +++ b/labextension/src/widgets/LeftPanel.tsx @@ -61,6 +61,7 @@ interface IProps { kernel: Kernel.IKernelConnection; enableKaleByDefault: boolean; autoSaveOnCompileOrRun: boolean; + defaultBaseImage?: string; } interface IState { @@ -789,6 +790,16 @@ export class KubeflowKaleLeftPanel extends React.Component { {pipeline_desc_input} {enable_caching_toggle} + { + this.setState({ defaultBaseImage: v }); + this.updateDockerImage(v); + }} + />
Date: Fri, 10 Apr 2026 12:32:13 +0530 Subject: [PATCH 6/8] fix: apply prettier formatting Signed-off-by: Vishal --- labextension/src/widget.tsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/labextension/src/widget.tsx b/labextension/src/widget.tsx index c81423409..7dbb57049 100644 --- a/labextension/src/widget.tsx +++ b/labextension/src/widget.tsx @@ -104,16 +104,16 @@ async function activate( // TODO: backend can become an Enum that indicates the type of // env we are in (like Local Laptop, MiniKF, GCP, UI without Kale, ...) const backend = await getBackend(kernel); - const settings = await settingRegistry.load('jupyterlab-kubeflow-kale:deploymentPanel'); + const settings = await settingRegistry.load( + 'jupyterlab-kubeflow-kale:deploymentPanel', + ); let defaultBaseImage = - (settings.get('default_base_image').composite as string) || - 'python:3.12'; + (settings.get('default_base_image').composite as string) || 'python:3.12'; settings.changed.connect(() => { defaultBaseImage = - (settings.get('default_base_image').composite as string) || - ''; + (settings.get('default_base_image').composite as string) || ''; if (kalePanelWidget) { const newWidget = createPanel(defaultBaseImage); @@ -138,7 +138,6 @@ async function activate( } } - async function loadPanel() { let reveal_widget = undefined; if (backend) { @@ -176,7 +175,7 @@ async function activate( enableKaleByDefault={false} autoSaveOnCompileOrRun={false} defaultBaseImage={defaultBaseImage} - /> + />, ); } // Creates the left side bar widget once the app has fully started From c6e9bc6279b61928476a1b622199ecda568cad1f Mon Sep 17 00:00:00 2001 From: Vishal Date: Fri, 10 Apr 2026 12:46:07 +0530 Subject: [PATCH 7/8] fix: add missing deploymentPanel schema and formatting Signed-off-by: Vishal --- labextension/schema/deploymentPanel.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 labextension/schema/deploymentPanel.json diff --git a/labextension/schema/deploymentPanel.json b/labextension/schema/deploymentPanel.json new file mode 100644 index 000000000..fb93f2ddd --- /dev/null +++ b/labextension/schema/deploymentPanel.json @@ -0,0 +1,13 @@ +{ + "title": "Kale Deployment Panel Settings", + "description": "Settings for default base image configuration", + "type": "object", + "properties": { + "defaultBaseImage": { + "type": "string", + "title": "Default Base Image", + "description": "Default Docker image used for pipeline steps", + "default": "python:3.12" + } + } +} \ No newline at end of file From 85294c31b7798709553792f1d6b598daa55fe496 Mon Sep 17 00:00:00 2001 From: Vishal Date: Fri, 10 Apr 2026 12:58:15 +0530 Subject: [PATCH 8/8] fix: format schema with prettier Signed-off-by: Vishal --- labextension/schema/deploymentPanel.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/labextension/schema/deploymentPanel.json b/labextension/schema/deploymentPanel.json index fb93f2ddd..a493be7d1 100644 --- a/labextension/schema/deploymentPanel.json +++ b/labextension/schema/deploymentPanel.json @@ -10,4 +10,4 @@ "default": "python:3.12" } } -} \ No newline at end of file +}