[object Object]

← back to Wallco Ai

admin/inspiration/generate: actionable 503 when Ollama is unreachable + OLLAMA_HOST fallback

77613994bd487b17f3aa63e088b4190bb9336971 · 2026-05-19 14:30:38 -0700 · SteveStudio2

Was failing silently as a generic 500 on prod because the default Ollama URL (192.168.1.133:11434) is Mac1's LAN address — not routable from Kamatera. Now: explicit OLLAMA_URL wins, then OLLAMA_HOST, then the LAN fallback; the fetch is wrapped so the browser sees the URL tried + the fix hint instead of a bare 500.

Files touched

Diff

commit 77613994bd487b17f3aa63e088b4190bb9336971
Author: SteveStudio2 <steve@designerwallcoverings.com>
Date:   Tue May 19 14:30:38 2026 -0700

    admin/inspiration/generate: actionable 503 when Ollama is unreachable + OLLAMA_HOST fallback
    
    Was failing silently as a generic 500 on prod because the default Ollama URL (192.168.1.133:11434) is Mac1's LAN address — not routable from Kamatera. Now: explicit OLLAMA_URL wins, then OLLAMA_HOST, then the LAN fallback; the fetch is wrapped so the browser sees the URL tried + the fix hint instead of a bare 500.
---
 src/admin.js | 45 ++++++++++++++++++++++++++++++++++-----------
 1 file changed, 34 insertions(+), 11 deletions(-)

diff --git a/src/admin.js b/src/admin.js
index b0ccbc8..c6ded1a 100644
--- a/src/admin.js
+++ b/src/admin.js
@@ -412,17 +412,40 @@ function mount(app) {
       ].join('\n');
       const userMsg = `Aesthetic inspiration (names only):\n${scrubbedList}\n\nReturn a JSON array of 3 prompts.`;
 
-      const ollamaUrl = process.env.OLLAMA_URL || 'http://192.168.1.133:11434';
-      const ollResp = await fetch(`${ollamaUrl}/api/chat`, {
-        method: 'POST', headers: { 'Content-Type': 'application/json' },
-        body: JSON.stringify({
-          model: process.env.OLLAMA_MODEL || 'qwen3:14b',
-          messages: [{ role: 'system', content: sys }, { role: 'user', content: userMsg }],
-          stream: false, options: { temperature: 0.7 }
-        }),
-        signal: AbortSignal.timeout(60_000)
-      });
-      const ollText = (await ollResp.json()).message?.content || '';
+      // Ollama URL — explicit OLLAMA_URL wins; else OLLAMA_HOST (host:port form
+      // used by the Mac1→Kamatera Tailscale bridge convention); else fall back to
+      // Mac1's LAN address, which prod CANNOT reach — so prod must set one of
+      // OLLAMA_URL / OLLAMA_HOST in wallco-ai/.env or the try/catch below fires
+      // with an actionable error (no more generic 500 on "Generate 3 originals").
+      const ollamaUrl = process.env.OLLAMA_URL
+        || (process.env.OLLAMA_HOST ? `http://${String(process.env.OLLAMA_HOST).replace(/^https?:\/\//, '')}` : null)
+        || 'http://192.168.1.133:11434';
+
+      let ollText;
+      try {
+        const ollResp = await fetch(`${ollamaUrl}/api/chat`, {
+          method: 'POST', headers: { 'Content-Type': 'application/json' },
+          body: JSON.stringify({
+            model: process.env.OLLAMA_MODEL || 'qwen3:14b',
+            messages: [{ role: 'system', content: sys }, { role: 'user', content: userMsg }],
+            stream: false, options: { temperature: 0.7 }
+          }),
+          signal: AbortSignal.timeout(60_000)
+        });
+        if (!ollResp.ok) {
+          return res.status(503).json({
+            error: 'ollama_http_error', status: ollResp.status, url: ollamaUrl,
+          });
+        }
+        ollText = (await ollResp.json()).message?.content || '';
+      } catch (e) {
+        return res.status(503).json({
+          error: 'ollama_unreachable',
+          url: ollamaUrl,
+          reason: String(e.message || e).slice(0, 200),
+          hint: 'Set OLLAMA_URL (or OLLAMA_HOST) in wallco-ai/.env. For prod use the Mac1→Kamatera Tailscale bridge: OLLAMA_URL=http://100.65.187.120:11435',
+        });
+      }
       let prompts = [];
       try {
         const m = ollText.match(/\[\s*"[\s\S]*?"\s*\]/);

← 48f6b40 Fuzzy-design cull pipeline: Gemini classifier + viewer + app  ·  back to Wallco Ai  ·  /design page: show full palette as paint chips + paint strip b6b1b00 →