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.js46
1 files changed, 40 insertions, 6 deletions
diff --git a/modules/core/ollama-manager.js b/modules/core/ollama-manager.js
index 4f47db2..3d157ba 100644
--- a/modules/core/ollama-manager.js
+++ b/modules/core/ollama-manager.js
@@ -369,11 +369,36 @@ class OllamaManager {
}
}
+ // ─── Lazy connect ────────────────────────────────────────────────────────
+ /**
+ * Every request path calls this first. Nothing connects at boot, so the user's first message
+ * used to die with "Use ollamaconnect first" even when the endpoint was healthy
+ * (reported 2026-10-26/27). One attempt, then the caller prints the real reason.
+ * Concurrent callers share the same in-flight promise.
+ */
+ async _ensureConnected() {
+ if (this.connected) return true;
+ if (this._connecting) {
+ try { await this._connecting; } catch (e) { /* fall through to the state check */ }
+ return this.connected;
+ }
+ this.terminal.print('Connecting to Ollama…', 'info');
+ this._connecting = this.connect();
+ try { await this._connecting; } catch (e) {
+ this.terminal.log('WARN', 'Ollama', 'Auto-connect failed: ' + e.message);
+ }
+ this._connecting = null;
+ if (!this.connected) {
+ this.terminal.print('Auto-connect failed — check the endpoint in the Ollama tab (or run ollamaconnect).', 'warning');
+ }
+ return this.connected;
+ }
+
// ─── Chat (non-streaming) ────────────────────────────────────────────────
async chat(prompt, model = null) {
- if (!this.connected) {
- this.terminal.print('Ollama not connected. Use ollamaconnect first.', 'error');
+ if (!this.connected && !(await this._ensureConnected())) {
+ this.terminal.print('Ollama not connected: the endpoint is unreachable. Check the Ollama tab or run ollamaconnect.', 'error');
return null;
}
const useModel = model || this.currentModel;
@@ -540,7 +565,10 @@ class OllamaManager {
// ─── Chat Stream ─────────────────────────────────────────────────────────
async chatStream(prompt, onToken, onComplete, model = null) {
- if (!this.connected) { this.terminal.print('Ollama not connected.', 'error'); onComplete && onComplete(null); return; }
+ if (!this.connected && !(await this._ensureConnected())) {
+ this.terminal.print('Ollama not connected: the endpoint is unreachable.', 'error');
+ onComplete && onComplete(null); return;
+ }
const useModel = model || this.currentModel;
if (!useModel) { this.terminal.print('No model selected.', 'error'); onComplete && onComplete(null); return; }
@@ -711,8 +739,8 @@ class OllamaManager {
}
async chatWithTools(messages, tools, model = null) {
- if (!this.connected) {
- this.terminal.print('Ollama not connected. Use ollamaconnect first.', 'error');
+ if (!this.connected && !(await this._ensureConnected())) {
+ this.terminal.print('Ollama not connected: the endpoint is unreachable. Check the Ollama tab or run ollamaconnect.', 'error');
return { message: null, tool_calls: null, error: 'Not connected' };
}
const useModel = model || this.currentModel;
@@ -893,7 +921,13 @@ class OllamaManager {
}
return {
- message: message ? { role: message.role || 'assistant', content: content } : null,
+ message: message ? {
+ role: message.role || 'assistant',
+ content: content,
+ // OpenAI-compatible providers require tool_calls INSIDE the assistant message;
+ // a bare { role:'tool' } with no preceding tool_calls is rejected with HTTP 400.
+ ...(Array.isArray(toolCalls) && toolCalls.length ? { tool_calls: toolCalls } : {}),
+ } : null,
tool_calls: toolCalls ? toolCalls.map(tc => ({
id: tc.id,
type: tc.type || 'function',