import { OllamaBaseBackend, OllamaMessage, OllamaChatResponse } from "./ollama-base.js"; import { OllamaLocalConfig } from "./types.js"; import { ToolRegistry } from "./tool-registry.js"; export class OllamaLocalBackend extends OllamaBaseBackend { readonly name = "ollama-local"; private localConfig: OllamaLocalConfig; constructor(config?: OllamaLocalConfig) { super(config); this.localConfig = config || { base_url: "http://localhost:11434", model_profile: "balanced" }; } async isAvailable(): Promise { try { const response = await fetch(`${this.localConfig.base_url}/api/tags`, { signal: AbortSignal.timeout(5000), }); return response.ok; } catch { return false; } } protected resolveModel(): string { if (this.localConfig.model) return this.localConfig.model; return this.modelProfileToModel(this.localConfig.model_profile, []); } protected async callModel( messages: OllamaMessage[], model: string, toolRegistry: ToolRegistry ): Promise { let resolvedModel = model; if (!this.localConfig.model) { const models = await this.fetchAvailableModels(); resolvedModel = this.modelProfileToModel(this.localConfig.model_profile, models); } const url = `${this.localConfig.base_url}/v1/chat/completions`; const body: Record = { model: resolvedModel, messages: messages.map((m) => { const msg: Record = { role: m.role, content: m.content }; if (m.name) msg.name = m.name; if (m.tool_calls) msg.tool_calls = m.tool_calls; return msg; }), tools: this.getActiveToolSchema(toolRegistry), stream: false, }; const timeout = this.localConfig.timeout_ms || 10000; const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), signal: AbortSignal.timeout(timeout), }); if (!response.ok) { const errorText = await response.text().catch(() => "unknown error"); throw new Error(`Ollama local API error (${response.status}): ${errorText}`); } return (await response.json()) as OllamaChatResponse; } }