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
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 @@