aboutsummaryrefslogtreecommitdiff
path: root/modules/core/ollama-manager.js
diff options
context:
space:
mode:
Diffstat (limited to 'modules/core/ollama-manager.js')
-rw-r--r--modules/core/ollama-manager.js58
1 files changed, 45 insertions, 13 deletions
diff --git a/modules/core/ollama-manager.js b/modules/core/ollama-manager.js
index 7dcecc3..1201280 100644
--- a/modules/core/ollama-manager.js
+++ b/modules/core/ollama-manager.js
@@ -2,6 +2,31 @@
// Refactored to support multiple LLM providers with auto-fallback
// Class name kept as OllamaManager for backward compatibility
class OllamaManager {
+
+ async _httpError(response) {
+ // Surface what the server actually said. "HTTP 429" tells the user nothing; the body
+ // carries e.g. "Demo limit reached. Login or sign up for more." (2026-10-28)
+ let detail = '';
+ try {
+ const text = await response.text();
+ if (text) {
+ try {
+ const j = JSON.parse(text);
+ detail = (j && (j.error || j.message)) || '';
+ if (j && j.needed_usd != null && j.balance_usd != null) {
+ detail += ` (balance $${j.balance_usd}, need $${j.needed_usd})`;
+ }
+ } catch (_) {
+ detail = text.slice(0, 200);
+ }
+ }
+ } catch (_) { /* body unreadable or already consumed */ }
+ if (/^\s*</.test(detail)) detail = ''; // an HTML error page is not a message
+ const err = new Error(detail ? `${detail} (HTTP ${response.status})` : `HTTP ${response.status}`);
+ err.status = response.status;
+ return err;
+ }
+
constructor(terminal) {
this.terminal = terminal;
// --- Backward-compat properties (reflected from active provider) ---
@@ -61,7 +86,14 @@ class OllamaManager {
// just told. Send it on every Ollama request; the OpenAI-compatible branch has its own params.
_ctxTokens() {
const n = Number(this.numCtx || (typeof localStorage !== 'undefined' ? localStorage.getItem('webx11_ollama_numctx') : 0));
- return Number.isFinite(n) && n >= 512 ? Math.floor(n) : 16384;
+ if (Number.isFinite(n) && n >= 512) return Math.floor(n);
+ // No explicit setting: take the model class default (large/cloud 65536, mid 32768, …)
+ try {
+ const mp = this.modelProfiles
+ || (typeof MODEL_PROFILES !== 'undefined' ? MODEL_PROFILES : null);
+ if (mp && mp.profileFor && this.currentModel) return mp.profileFor(this.currentModel).ctx;
+ } catch (e) {}
+ return 16384;
}
_ollamaOptions(extra) {
@@ -261,7 +293,7 @@ class OllamaManager {
const response = await fetch(`${ep}/api/tags`, {
method: 'GET', headers: { 'Content-Type': 'application/json' }
});
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
+ if (!response.ok) throw await this._httpError(response);
const data = await response.json();
this.models = data.models || [];
this.connected = true;
@@ -298,7 +330,7 @@ class OllamaManager {
const response = await fetch(`${provider.endpoint}/v1/models`, {
method: 'GET', headers
});
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
+ if (!response.ok) throw await this._httpError(response);
const data = await response.json();
this.models = (data.data || data.models || []).map(m => ({
name: m.id || m.name,
@@ -336,7 +368,7 @@ class OllamaManager {
const response = await fetch(`${ep}/api/tags`, {
method: 'GET', headers: { 'Content-Type': 'application/json' }
});
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
+ if (!response.ok) throw await this._httpError(response);
const data = await response.json();
this.models = data.models || [];
this.connected = true;
@@ -492,7 +524,7 @@ class OllamaManager {
body: JSON.stringify({ model: useModel, prompt: prompt, stream: this.streamEnabled, options: this._ollamaOptions() }),
signal
});
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
+ if (!response.ok) throw await this._httpError(response);
if (this.streamEnabled) {
const reader = response.body.getReader();
@@ -549,7 +581,7 @@ class OllamaManager {
}),
signal
});
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
+ if (!response.ok) throw await this._httpError(response);
if (this.streamEnabled) {
const reader = response.body.getReader();
@@ -662,7 +694,7 @@ class OllamaManager {
body: JSON.stringify({ model: useModel, prompt: prompt, stream: this.streamEnabled, options: this._ollamaOptions() }),
signal
});
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
+ if (!response.ok) throw await this._httpError(response);
let fullResponse = '';
if (this.streamEnabled) {
@@ -725,7 +757,7 @@ class OllamaManager {
}),
signal
});
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
+ if (!response.ok) throw await this._httpError(response);
let fullResponse = '';
if (this.streamEnabled) {
@@ -850,7 +882,7 @@ class OllamaManager {
if (response.status === 400) {
return await this.chatWithToolsFallback(messages, tools, useModel, signal);
}
- throw new Error(`HTTP ${response.status}`);
+ throw await this._httpError(response);
}
const data = await response.json();
this.terminal.log('INFO', 'Ollama', `chatWithTools round-trip: ${networkTime}s (model: ${useModel}, provider: ${provider.name})`);
@@ -900,7 +932,7 @@ class OllamaManager {
});
const networkTime = Math.round((Date.now() - t0) / 1000);
if (!response.ok) {
- throw new Error(`HTTP ${response.status}`);
+ throw await this._httpError(response);
}
const data = await response.json();
this.terminal.log('INFO', 'Ollama', `chatWithTools round-trip: ${networkTime}s (model: ${useModel}, provider: ${provider.name})`);
@@ -1039,7 +1071,7 @@ class OllamaManager {
body: JSON.stringify(body),
signal
});
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
+ if (!response.ok) throw await this._httpError(response);
const data = await response.json();
const text = data.choices?.[0]?.message?.content || '';
const latency = Date.now() - t0;
@@ -1075,7 +1107,7 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body), signal
});
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
+ if (!response.ok) throw await this._httpError(response);
const data = await response.json();
const text = (data.message?.content || '').trim();
const latency = Date.now() - t0;
@@ -1126,7 +1158,7 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm
body: JSON.stringify(body),
signal
});
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
+ if (!response.ok) throw await this._httpError(response);
const data = await response.json();
const text = data.message?.content || data.response || '';
const latency = Date.now() - t0;