[object Object]

← back to Corkwallcovering

cost-saver: local-first ollama enrichment (vocab-gated fallback)

0f095000ce4e8970774ab487fa8cc8fdaa0f4990 · 2026-07-03 09:12:53 -0700 · Steve

Files touched

Diff

commit 0f095000ce4e8970774ab487fa8cc8fdaa0f4990
Author: Steve <steve@designerwallcoverings.com>
Date:   Fri Jul 3 09:12:53 2026 -0700

    cost-saver: local-first ollama enrichment (vocab-gated fallback)
---
 scripts/enrich-cork-ai.mjs | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/scripts/enrich-cork-ai.mjs b/scripts/enrich-cork-ai.mjs
index 7f4723d..cbc9a74 100644
--- a/scripts/enrich-cork-ai.mjs
+++ b/scripts/enrich-cork-ai.mjs
@@ -64,6 +64,23 @@ async function askGemini(key, img, need, title) {
     `(title: "${title}"). Return STRICT JSON with exactly these keys: { ${asks.join(', ')} }. ` +
     `Pick the single best-fitting value from each list — no other words, no explanation. ` +
     `Most of these are cork / mica / natural-fiber textured wallcoverings.`;
+  // LOCAL-FIRST cost-saver (2026-07-03): free ollama qwen2.5vl before paid Gemini. ENRICH_GEMINI_ONLY=1 forces Gemini.
+  if (process.env.ENRICH_GEMINI_ONLY !== '1') {
+    try {
+      const lr = await fetch('http://127.0.0.1:11434/api/generate', {
+        method: 'POST', headers: { 'Content-Type': 'application/json' },
+        body: JSON.stringify({ model: process.env.OLLAMA_VISION_MODEL || 'qwen2.5vl:7b', prompt,
+          images: [img.data], format: 'json', stream: false, options: { temperature: 0 } }),
+        signal: AbortSignal.timeout(90000) });
+      if (lr.ok) { const lj = await lr.json();
+        let lout = {}; try { lout = JSON.parse((lj.response || '{}').replace(/^```json\s*|\s*```$/g, '').trim()); } catch (_) {}
+        const ladd = [];
+        if (wantStyle && STYLE_VOCAB.includes(lout.style)) ladd.push(lout.style);
+        if (wantPattern && PATTERN_VOCAB.includes(lout.pattern)) ladd.push(lout.pattern);
+        if (ladd.length) return ladd;   // accept local only if it produced valid-vocab tags; else fall through to Gemini
+      }
+    } catch (_) { /* fall through to Gemini */ }
+  }
   const body = {
     contents: [{ parts: [ { text: prompt }, { inline_data: { mime_type: img.mime, data: img.data } } ] }],
     generationConfig: { temperature: 0, responseMimeType: 'application/json' },

← 21b50ec chore: macstudio3 migration — reconcile from mac2 + repoint  ·  back to Corkwallcovering  ·  corner-nav: move login to its own hamburger in upper-left (p a1a004d →