diff --git a/simpletuner/simpletuner_sdk/server/services/tab_service.py b/simpletuner/simpletuner_sdk/server/services/tab_service.py index 779739286..2e7de3776 100644 --- a/simpletuner/simpletuner_sdk/server/services/tab_service.py +++ b/simpletuner/simpletuner_sdk/server/services/tab_service.py @@ -508,7 +508,12 @@ def _git_mirror_enabled(self) -> bool: def _cloud_tab_enabled(self) -> bool: try: defaults = WebUIStateStore().load_defaults() - return bool(getattr(defaults, "cloud_tab_enabled", True)) + value = getattr(defaults, "cloud_tab_enabled", True) + if value is None: + return True + if isinstance(value, str): + return value.strip().lower() not in {"0", "false", "no", "off"} + return bool(value) except Exception as exc: logger.debug("Failed to evaluate cloud tab enabled flag: %s", exc, exc_info=True) return True diff --git a/simpletuner/simpletuner_sdk/server/services/webui_state.py b/simpletuner/simpletuner_sdk/server/services/webui_state.py index 857cc3429..fe5704cf8 100644 --- a/simpletuner/simpletuner_sdk/server/services/webui_state.py +++ b/simpletuner/simpletuner_sdk/server/services/webui_state.py @@ -331,12 +331,13 @@ def load_defaults(self) -> WebUIDefaults: payload = self._read_json("defaults") if not payload: return WebUIDefaults() + base_defaults = WebUIDefaults() data: Dict[str, Any] = {} - for key in WebUIDefaults().__dict__.keys(): + for key, default_value in base_defaults.__dict__.items(): if key == "accelerate_overrides": data[key] = _normalise_accelerate_overrides(payload.get(key)) else: - data[key] = payload.get(key) + data[key] = payload.get(key, default_value) defaults = WebUIDefaults(**data) # Normalise theme selection @@ -429,6 +430,17 @@ def load_defaults(self) -> WebUIDefaults: defaults.cloud_dataloader_hint_dismissed = bool(payload.get("cloud_dataloader_hint_dismissed", False)) defaults.cloud_git_hint_dismissed = bool(payload.get("cloud_git_hint_dismissed", False)) + # Normalise cloud tab enabled (default True) + cloud_tab_value = payload.get("cloud_tab_enabled") + if cloud_tab_value is None: + defaults.cloud_tab_enabled = True + elif isinstance(cloud_tab_value, bool): + defaults.cloud_tab_enabled = cloud_tab_value + elif isinstance(cloud_tab_value, str): + defaults.cloud_tab_enabled = cloud_tab_value.strip().lower() not in {"0", "false", "no", "off"} + else: + defaults.cloud_tab_enabled = bool(cloud_tab_value) + # Normalise cloud data consent consent_value = payload.get("cloud_data_consent") if isinstance(consent_value, str) and consent_value in {"ask", "allow", "deny"}: diff --git a/simpletuner/static/js/modules/cloud/index.js b/simpletuner/static/js/modules/cloud/index.js index 8c42cc256..63e0e1cbe 100644 --- a/simpletuner/static/js/modules/cloud/index.js +++ b/simpletuner/static/js/modules/cloud/index.js @@ -413,7 +413,8 @@ if (!window.cloudDashboardComponent) { this.setupStatus.outputConfigured = this.publishingStatus.push_to_hub || this.publishingStatus.s3_configured || - (this.webhookUrl && this.webhookUrl.trim().length > 0); + (this.savedWebhookUrl && this.savedWebhookUrl.trim().length > 0) || + this.publishingStatus.local_upload_available; }, // Note: hasDatasets getter moved to final return object @@ -677,7 +678,8 @@ if (!window.cloudDashboardComponent) { if (!this.publishingStatus) return false; return this.publishingStatus.push_to_hub || this.publishingStatus.s3_configured || - (this.webhookUrl && this.webhookUrl.trim().length > 0); + (this.savedWebhookUrl && this.savedWebhookUrl.trim().length > 0) || + this.publishingStatus.local_upload_available; }, get allSetupComplete() { return this.hasDatasets && this.hasActiveConfig && this.hasOutputDestination; diff --git a/simpletuner/static/js/modules/cloud/metrics.js b/simpletuner/static/js/modules/cloud/metrics.js index d3c1204df..a04aa1748 100644 --- a/simpletuner/static/js/modules/cloud/metrics.js +++ b/simpletuner/static/js/modules/cloud/metrics.js @@ -59,19 +59,42 @@ window.cloudMetricsMethods = { async saveWebhookConfig() { this.configSaving = true; + const webhookUrl = typeof this.webhookUrl === 'string' ? this.webhookUrl.trim() : ''; try { const response = await fetch('/api/cloud/providers/replicate/config', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ webhook_url: this.webhookUrl || null }), + body: JSON.stringify({ webhook_url: webhookUrl }), }); - if (response.ok && window.showToast) { + + let data = {}; + try { + data = await response.json(); + } catch (_) {} + + if (!response.ok) { + throw new Error(data.detail || 'Failed to save webhook config'); + } + + const savedUrl = (data.config && data.config.webhook_url) || data.webhook_url || webhookUrl || ''; + this.savedWebhookUrl = savedUrl; + this.webhookUrl = savedUrl; + if (this.publishingStatus) { + this.publishingStatus.local_upload_available = savedUrl.length > 0; + if (!savedUrl) { + this.publishingStatus.local_upload_dir = null; + } + } + + if (window.showToast) { window.showToast('Webhook configuration saved', 'success'); } + return true; } catch (error) { if (window.showToast) { - window.showToast('Failed to save webhook config', 'error'); + window.showToast(error.message || 'Failed to save webhook config', 'error'); } + return false; } finally { this.configSaving = false; } diff --git a/simpletuner/static/js/modules/cloud/providers.js b/simpletuner/static/js/modules/cloud/providers.js index 46f93508c..9f2e75f4d 100644 --- a/simpletuner/static/js/modules/cloud/providers.js +++ b/simpletuner/static/js/modules/cloud/providers.js @@ -27,8 +27,9 @@ window.cloudProviderMethods = { const response = await fetch(`/api/cloud/providers/${this.activeProvider}/config`); if (response.ok) { const data = await response.json(); - this.providerConfig = data || {}; - this.webhookUrl = data.webhook_url || ''; + this.providerConfig = data.config || data || {}; + this.savedWebhookUrl = this.providerConfig.webhook_url || ''; + this.webhookUrl = this.savedWebhookUrl; this.loadCostLimitStatus(); } } catch (error) { diff --git a/simpletuner/static/js/modules/cloud/state/publishing-state.js b/simpletuner/static/js/modules/cloud/state/publishing-state.js index 448d610c6..aa87ba7f9 100644 --- a/simpletuner/static/js/modules/cloud/state/publishing-state.js +++ b/simpletuner/static/js/modules/cloud/state/publishing-state.js @@ -10,6 +10,7 @@ window.cloudPublishingStateFactory = function(initial) { availableConfigs: [], selectedConfigName: null, webhookUrl: initialData.webhook_url || '', + savedWebhookUrl: initialData.webhook_url || '', webhookTesting: false, webhookTestMode: null, webhookTestResult: null, diff --git a/simpletuner/templates/cloud_tab.html b/simpletuner/templates/cloud_tab.html index e262b3936..c342c356a 100644 --- a/simpletuner/templates/cloud_tab.html +++ b/simpletuner/templates/cloud_tab.html @@ -139,7 +139,7 @@
Local Outputs
+ x-text="savedWebhookUrl.replace(/\/$/, '') + '/api/cloud/storage'">
diff --git a/simpletuner/templates/partials/cloud_dataloader_hint.html b/simpletuner/templates/partials/cloud_dataloader_hint.html index e901c41a3..f80782f31 100644 --- a/simpletuner/templates/partials/cloud_dataloader_hint.html +++ b/simpletuner/templates/partials/cloud_dataloader_hint.html @@ -141,7 +141,7 @@
-