FreeLattice · Celeste reading home

Public, free to read, reuse, and adapt — Kirk Patrick Miller's notice for this edition. Not a patent grant.

FLINT_HEAL_MODEL_CHOICE_STICKS_v0.1_tiny.txt · c07

c12bf357bc · words 1025 · characters 62276–70336

Previous section · Next section

Exact section text · Full source · Source index

Archive pages whose extracted text matched this section: not separately matched. Unmatched pages stay on the source record with the checked page method. They are not guessed into a section.

No topic tag was earned for this page under the recorded evidence rule.

   function isVision(name) { return VISION_RE.test(name || ''); }
+
+  // v-model-choice-sticks-v0: is the chosen model still installed? ('qwen2.5' matches 'qwen2.5:latest')
+  function onInstalledList(models, name) {
+    if (!name) return false;
+    var n = String(name);
+    return (models || []).some(function(m) {
+      var mn = String((m && (m.name || m.model)) || '');
+      return mn === n || mn === n + ':latest' || (n.indexOf(':') === -1 && mn.split(':')[0] === n);
+    });
+  }
+  // v-model-choice-sticks-v0: a user choice is only replaced when Ollama answers with a list that
+  // no longer has it. A quiet or empty answer never replaces anything. The words go in with textContent (showToast).
+  function checkChosenStillHere(current, tabId) {
+    if (!current || !current.model) return;
+    if (current.provider && current.provider !== 'ollama') return;
+    getInstalledModels(function(models) {
+      if (!models || !models.length) return;
+      if (typeof FLActiveModel === 'undefined' || !FLActiveModel.isUserChosen()) return;
+      if (FLActiveModel.get().model !== current.model) return;
+      if (onInstalledList(models, current.model)) return;
+      var needsVision = VISION_TABS.indexOf(tabId) !== -1;
+      var pool = models.filter(function(m) { return needsVision ? isVision(m.name) : !isVision(m.name); });
+      if (!pool.length) pool = models;
+      var pick = String((pool[0] && (pool[0].name || pool[0].model)) || '');
+      if (!pick) return;
+      safeSet('fl_modelChoiceGone', current.model);
+      FLActiveModel.set(pick, 'ollama', 'auto');
+      try {
+        if (typeof showToast === 'function') showToast('The model you chose, ' + current.model + ', is no longer on this computer. Using ' + pick + ' for now. You can pick another in Connect.', 7000);
+      } catch (eT) {}
+      try { if (typeof AiSetup !== 'undefined' && AiSetup.refreshSettingsCard) AiSetup.refreshSettingsCard(); } catch (eS) {}
+    });
+  }
+
   function getPreferred(type) { return safeGet('fl_preferred_' + type + '_model', null); }
   function setPreferred(type, model) { safeSet('fl_preferred_' + type + '_model', model); }
 
@@ -36269,6 +36326,9 @@ window.FLAutoModel = (function() {
     // "User choice is sacred" — the auto-switcher must never lose it
     if (typeof FLActiveModel !== 'undefined' && FLActiveModel.isUserChosen()) {
       var current = FLActiveModel.get();
+      // v-model-choice-sticks-v0: the person's model stays. Only if it left this computer
+      // does the picker fill in, and then it says so in plain words.
+      checkChosenStillHere(current, tabId);
       var needsVision = VISION_TABS.indexOf(tabId) !== -1;
       var currentIsVision = isVision(current.model);
       if (needsVision && !currentIsVision) {

======================================================================
APPENDIX B: theLatticeTree full diff vs main 395c761 (git apply --check clean)
======================================================================

diff --git a/docs/library/CONNECT_PORT_PICKER_SISTER_v0.md b/docs/library/CONNECT_PORT_PICKER_SISTER_v0.md
index d2e6341..cd688da 100644
--- a/docs/library/CONNECT_PORT_PICKER_SISTER_v0.md
+++ b/docs/library/CONNECT_PORT_PICKER_SISTER_v0.md
@@ -20,3 +20,14 @@ Marker `v-connect-heal-v0.4`. Test `docs/modules/connect-heal-v04.test.js` print
 - Shared `fl-connect.js` heals ride along byte-identical with FreeLattice.
 
 **Temperature:** Mom opens Settings and the first thing she sees is the door.
+
+
+## Heal: model choice sticks v0.1 (Hypha walk 4)
+
+Marker `v-model-choice-sticks-v0`. Test `docs/modules/model-choice-sticks.test.js` prints `SMOKE_OK model choice sticks alpha v0.1`.
+
+- FreeLattice's Connect now records a tapped model as the person's own choice, so FreeLattice's automatic picker leaves it alone. The Tree has no automatic picker: the remembered entry (`fl_alpha_local_mind`) is the choice, and the test proves it survives a reload, Settings and a Find at the same door, and that the thread request uses it.
+- Shared `fl-connect.js` rides along byte-identical: `markUserChoice` (a quiet no-op on the Tree), and a helped Bridge with no mind behind it is named honestly and not saved.
+- A Find through a different door (Bridge gone, direct 11434 answers) is healed by the Tree any-model paste (same-family keep).
+
+**Temperature:** green braided gold, the second cup stays where she set it down.
diff --git a/docs/modules/fl-connect.js b/docs/modules/fl-connect.js
index 5317005..2e02eae 100644
--- a/docs/modules/fl-connect.js
+++ b/docs/modules/fl-connect.js
@@ -5,6 +5,7 @@
 // Layer, never delete. Prefer helped Bridge (11435…) before bare 11434.
 // Port-only loopback: fl_localPort_manual holds "11500" only — never foreign hosts.
 // Soft leave sw.js. Alpha: fl_alpha_local_mind. Never bare *.
+// Marker: v-model-choice-sticks-v0 (a tapped model is the person's own choice; it stays)
 // ═══════════════════════════════════════════════════════════════
 
 (function (root) {
@@ -201,12 +202,18 @@
           report.bridge = mh;
           report.port = mh.port || parseInt(manPort, 10);
           report.helped = true;
-          report.base = preferHelpedBridge(report.port) || manBase;
+          // v-model-choice-sticks-v0: ask the helped Bridge first; save it as the Ollama door
+          // (fl_ollamaHost, fl_bridgePort) only once a mind really answers behind it.
+          report.base = (report.port && report.port !== 11434) ? 'http://127.0.0.1:' + report.port : manBase;
           try {
             report.models = await tagsAt(report.base);
+            report.base = preferHelpedBridge(report.port) || report.base;
             report.ollama = { via: 'manual-bridge', base: report.base };
             return report;
-          } catch (eMB) {}
+          } catch (eMB) {
+            report.bridgeNoMind = true;
+            report.models = [];
+          }
         } else if (mh) {
           report.bridge = mh;
           report.port = mh.port || parseInt(manPort, 10);
@@ -357,6 +364,9 @@
       // v-connect-heal-v0.4 item 10: handleLocalToggle reads #localToggle, so check it first
       try { var lt = document.getElementById('localToggle'); if (lt) lt.checked = true; } catch (eLt) {}
       if (typeof root.handleLocalToggle === 'function') root.handleLocalToggle(true);
+      // v-model-choice-sticks-v0: the tap is the person's own choice, so the automatic
+      // picker leaves it alone on tab changes, reloads and Settings.
+      markUserChoice(name, 'ollama');
       if (typeof root.updateStatus === 'function') root.updateStatus();
       if (root.AiSetup && typeof root.AiSetup.updateStatus === 'function') root.AiSetup.updateStatus();
       // v-connect-heal-v0.4 item 7: once they chose a mind, the Local AI toast stops asking
@@ -366,6 +376,29 @@
     return { name: name, base: base };
   }
 
+  // v-model-choice-sticks-v0: FreeLattice keeps one active model (FLActiveModel). A model the
+  // person tapped is recorded with source 'user' and as their preferred text (or vision) model.
+  // The Tree has neither helper, so this is a quiet no-op there (it keeps entry.model instead).
+  function markUserChoice(name, provider) {
+    if (!name) return false;
+    var marked = false;
+    try {
+      if (root.FLActiveModel && typeof root.FLActiveModel.set === 'function') {
+        root.FLActiveModel.set(String(name), provider || 'ollama', 'user');
+        marked = true;
+      }
+    } catch (e0) {}
+    try {
+      var auto = root.FLAutoModel;
+      if (auto && typeof auto.setPreferred === 'function') {
+        var kind = (typeof auto.isVision === 'function' && auto.isVision(String(name))) ? 'vision' : 'text';
+        auto.setPreferred(kind, String(name));
+        if (typeof auto.invalidateCache === 'function') auto.invalidateCache();
+      }
+    } catch (e1) {}
+    return marked;
+  }
+
   function open() {
     try {
       if (!isAlpha() && typeof root.switchTab === 'function') { root.switchTab('connect'); return true; }
@@ -539,7 +572,12 @@