diff --git a/static/index.html b/static/index.html
index c0fd60197..34195ca38 100644
--- a/static/index.html
+++ b/static/index.html
@@ -219,8 +219,8 @@
}, { once: true });
})();
-
-
+
+
@@ -2512,7 +2512,7 @@
-
+
diff --git a/static/js/modelPicker.js b/static/js/modelPicker.js
index 12fb3479e..53077b73b 100644
--- a/static/js/modelPicker.js
+++ b/static/js/modelPicker.js
@@ -92,6 +92,31 @@ function _modelExists(modelId, url) {
});
}
+function _firstAvailableModel() {
+ if (!window.modelsModule || !window.modelsModule.getCachedItems) return null;
+ const items = window.modelsModule.getCachedItems() || [];
+ for (const item of items) {
+ if (item.offline) continue;
+ const models = (item.models || []).concat(item.models_extra || []);
+ if (!models.length) continue;
+ return {
+ url: item.url,
+ modelId: models[0],
+ endpointId: item.endpoint_id || '',
+ };
+ }
+ return null;
+}
+
+async function _ensureModelCacheForFallback() {
+ if (!window.modelsModule || !window.modelsModule.getCachedItems) return;
+ const items = window.modelsModule.getCachedItems() || [];
+ if (items.length) return;
+ if (typeof window.modelsModule.refreshModels === 'function') {
+ try { await window.modelsModule.refreshModels(false); } catch (_) {}
+ }
+}
+
async function _ensureDefaultPendingChat() {
if (!_deps || _defaultChatPickInFlight) return;
if (_deps.getCurrentSessionId && _deps.getCurrentSessionId()) return;
@@ -99,12 +124,13 @@ async function _ensureDefaultPendingChat() {
if (pending && pending.modelId) return;
_defaultChatPickInFlight = true;
try {
+ await _ensureModelCacheForFallback();
let dc = null;
try {
const res = await fetch(`${API_BASE}/api/default-chat`, { credentials: 'same-origin' });
if (res.ok) dc = await res.json();
} catch (_) {}
- if (dc && dc.endpoint_url && dc.model) {
+ if (dc && dc.endpoint_url && dc.model && _modelExists(dc.model, dc.endpoint_url)) {
_deps.setPendingChat({
url: dc.endpoint_url,
modelId: dc.model,
@@ -114,15 +140,12 @@ async function _ensureDefaultPendingChat() {
updateModelPicker();
return;
}
- // No configured default: preserve the old convenience fallback.
- if (window.modelsModule && window.modelsModule.getCachedItems) {
- const items = window.modelsModule.getCachedItems();
- const first = items.find(item => !item.offline && ((item.models || []).length || (item.models_extra || []).length));
- if (first) {
- const models = (first.models || []).concat(first.models_extra || []);
- _deps.setPendingChat({ url: first.url, modelId: models[0], endpointId: first.endpoint_id });
- updateModelPicker();
- }
+ // No configured default, or the configured default is gone/offline:
+ // preserve the convenience fallback and keep the picker usable.
+ const fallback = _firstAvailableModel();
+ if (fallback) {
+ _deps.setPendingChat(fallback);
+ updateModelPicker();
}
} finally {
_defaultChatPickInFlight = false;
diff --git a/static/sw.js b/static/sw.js
index 8e8930e9d..3df123bce 100644
--- a/static/sw.js
+++ b/static/sw.js
@@ -7,7 +7,7 @@
// - Other static assets (images/fonts/libs): cache-first with bg refresh.
// - API / non-GET: never cached.
// Bump CACHE_NAME whenever the precache list or SW logic changes.
-const CACHE_NAME = 'odysseus-v333';
+const CACHE_NAME = 'odysseus-v334';
// Core shell precached on install so repeat opens are instant without any
// network wait. Keep this list in sync with the