aboutsummaryrefslogtreecommitdiff
path: root/modules/test/test-ollama-autoconnect.js
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-10-27 14:41:20 +0100
committerAgando <roduccomm@gmail.com>2026-10-27 14:41:20 +0100
commitd2ec6fe99cc6f79013e689732fdc7f77089a8ed7 (patch)
treebfff314d01267162face828af2b7910bdae1a8c6 /modules/test/test-ollama-autoconnect.js
parentabe6cd64666e1fa61cc79ea4392cd78339e581d8 (diff)
fix(newos4): name the real Ollama failure, bound auto-connect, fix the review findings
Second pass over v4.6.105, driven by an independent review that reproduced each item. - ollama-manager.js: chat() now reports the captured cause ("Ollama error: Failed to fetch", "…: HTTP 500") instead of the generic "No provider responded"; _lastChatError is reset per call so an old reason is never replayed. chatWithTools carries the reason in its error string. - ollama-manager.js: _ensureConnected() bounds connect() with a deadline (connectTimeoutMs, default 10 s). connect() uses a bare fetch(), so a blackholed endpoint previously left the shared in-flight promise pending forever and hung every later message — the old code failed fast. The attempt is now try/finally, so a throwing terminal.log() can no longer poison _connecting and suppress the retry on the next message. - ollama-manager.js: an automatic connect no longer silently rewrites the user's saved model; the previous choice is restored in memory and in localStorage, and the user is told. - ollama-manager.js: the DeepSeek empty-content branch returned top-level tool_calls with content '', so the caller pushed an assistant message with no tool_calls and then a role:'tool' reply — an orphan tool message, which is the HTTP 400 that branch exists to prevent. tool_calls now live inside the message as well. - module-aiassist.js: a no-argument list_files returned the ROOT instead of the cwd (readDir('/') instead of ls()); it now means the current directory again. Error text keeps the path as the caller wrote it, and write_file distinguishes a read-only virtual path (/sys/…) from a missing parent instead of blaming the parent. Tests: test-ollama-error-text.js (new, 14 asserts, RED 9/5 before the edit), test-ai-fs-tools.js 7 → 16 asserts (write onto a directory, /sys write, list a file, '/', no-arg cwd, relative path), test-ollama-autoconnect.js 11 → 19 (blackholed endpoint settles + retries, saved model survives; the old "connecting hint" assertion is now falsifiable — the reviewer showed it also matched connect()'s pre-existing line). Suite: 10/10 files green. Live on v4.6.106: healthy auto-connect 42 ms with the hint; blackholed endpoint settles in 1242 ms with connectTimeoutMs=1200, prints "timed out" and "unreachable", _connecting cleared.
Diffstat (limited to 'modules/test/test-ollama-autoconnect.js')
-rw-r--r--modules/test/test-ollama-autoconnect.js34
1 files changed, 33 insertions, 1 deletions
diff --git a/modules/test/test-ollama-autoconnect.js b/modules/test/test-ollama-autoconnect.js
index b9db4ff..12b0786 100644
--- a/modules/test/test-ollama-autoconnect.js
+++ b/modules/test/test-ollama-autoconnect.js
@@ -59,7 +59,7 @@ const json = (body, okFlag = true) => Promise.resolve({ ok: okFlag, status: okFl
const res = await om.chat('ping');
const text = out.map(o => o.text).join(' | ');
ok('A1 connect is attempted lazily', seen.some(u => u.includes('/api/tags')), seen.join(', '));
- ok('A2 the user sees a connecting hint', /Connecting to Ollama/.test(text), text.slice(0, 160));
+ ok('A2 the user sees a first-use hint', /connecting on first use/.test(text), text.slice(0, 160));
ok('A3 manager is connected after the attempt', om.connected === true);
ok('A4 the message is answered', String(res).includes('pong'), String(res).slice(0, 120));
ok('A5 no "ollamaconnect first" error', !/ollamaconnect first/.test(text), text.slice(0, 200));
@@ -89,6 +89,38 @@ const json = (body, okFlag = true) => Promise.resolve({ ok: okFlag, status: okFl
ok('C1 three concurrent messages probe /api/tags once', tagCalls === 1, 'tagCalls=' + tagCalls);
}
+ // D. a blackholed endpoint must not hang every later message
+ {
+ const { om, out } = mk(() => new Promise(() => {})); // never settles
+ om.connectTimeoutMs = 100;
+ const t0 = Date.now();
+ const res = await om.chat('ping');
+ const ms = Date.now() - t0;
+ const text = out.map(o => o.text).join(' | ');
+ ok('D1 chat settles on a silent endpoint', ms < 3000, ms + 'ms');
+ ok('D2 returns null', res === null, String(res));
+ ok('D3 the timeout is reported', /timed out/.test(text), text.slice(0, 220));
+ ok('D4 _connecting is cleared', om._connecting === null, String(om._connecting));
+ const before = out.length;
+ await om.chat('again');
+ ok('D5 the next message retries the connect', out.length > before, 'lines ' + before + ' -> ' + out.length);
+ }
+
+ // E. an automatic connect must not rewrite the model the user saved
+ {
+ store['webx11_ollama_model'] = 'my-saved-model';
+ const { om, out } = mk((url) => url.includes('/api/tags')
+ ? json({ models: [{ name: 'other-model' }] })
+ : json({ response: 'x', eval_count: 1, prompt_eval_count: 1 }));
+ om.currentModel = 'my-saved-model';
+ await om._ensureConnected();
+ const text = out.map(o => o.text).join(' | ');
+ ok('E1 the saved model survives an auto-connect', om.currentModel === 'my-saved-model', om.currentModel);
+ ok('E2 the stored preference is restored', store['webx11_ollama_model'] === 'my-saved-model', String(store['webx11_ollama_model']));
+ ok('E3 the user is told why', /Keeping your saved model/.test(text), text.slice(0, 200));
+ delete store['webx11_ollama_model'];
+ }
+
if (fails === 0) {
console.log('test-ollama-autoconnect: ' + passes + ' passed, 0 failed');
} else {