mcard-js
Version:
MCard - Content-addressable storage with cryptographic hashing, handle resolution, and vector search for Node.js and browsers
158 lines • 5.97 kB
JavaScript
/**
* Ollama Provider Implementation
*
* Provider for the Ollama local LLM service.
* Replicates mcard.ptr.core.llm.providers.ollama.OllamaProvider
*/
import { BaseLLMProvider } from './LLMProvider.js';
import { Either } from '../../../monads/Either.js';
import { LLM_PROVIDERS } from '../Config.js';
import * as http from 'http';
import * as https from 'https';
export class OllamaProvider extends BaseLLMProvider {
provider_name = 'ollama';
base_url;
timeout;
config;
constructor(base_url = null, timeout = 180) {
super();
this.config = LLM_PROVIDERS['ollama'];
this.base_url = (base_url || this.config.base_url).replace(/\/$/, '');
this.timeout = timeout * 1000;
}
async _make_request(endpoint, data = null, method = 'POST') {
const urlStr = `${this.base_url}${endpoint}`;
const url = new URL(urlStr);
const isHttps = url.protocol === 'https:';
const client = isHttps ? https : http;
const options = {
method,
headers: {
'Content-Type': 'application/json',
},
timeout: this.timeout,
};
return new Promise((resolve) => {
const req = client.request(url, options, (res) => {
let body = '';
res.on('data', (chunk) => {
body += chunk;
});
res.on('end', () => {
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
try {
if (body.includes('\n') && !body.trim().startsWith('{')) {
resolve(Either.right(JSON.parse(body)));
}
else {
resolve(Either.right(JSON.parse(body)));
}
}
catch (e) {
const lines = body.trim().split('\n').filter(l => l);
if (lines.length > 0) {
try {
resolve(Either.right(JSON.parse(lines[lines.length - 1])));
}
catch (parseErr) {
resolve(Either.left(`Ollama response parse error: ${parseErr}`));
}
}
else {
resolve(Either.left(`Ollama response parse error: ${e}`));
}
}
}
else {
resolve(Either.left(`Ollama HTTP error ${res.statusCode}: ${body}`));
}
});
});
req.on('error', (e) => {
resolve(Either.left(`Ollama connection error: ${e.message}`));
});
req.on('timeout', () => {
req.destroy();
resolve(Either.left(`Ollama request timed out after ${this.timeout}ms`));
});
if (data) {
req.write(JSON.stringify(data));
}
req.end();
});
}
async complete(prompt, params) {
const data = {
model: params.model || this.config.default_model,
prompt: prompt,
stream: false,
};
if (params.options) {
data.options = params.options;
}
const result = await this._make_request(this.config.api_path, data);
if (result.isLeft) {
return Either.left(result.left);
}
const response = result.right;
if (response.response !== undefined) {
return Either.right(response.response);
}
else if (response.error) {
return Either.left(`Ollama error: ${response.error}`);
}
else {
return Either.left(`Unexpected Ollama response format: ${JSON.stringify(response)}`);
}
}
async chat(messages, params) {
const data = {
model: params.model || this.config.default_model,
messages: messages,
stream: false,
};
if (params.options) {
data.options = params.options;
}
const result = await this._make_request(this.config.chat_path, data);
if (result.isLeft) {
return Either.left(result.left);
}
const response = result.right;
if (response.message) {
return Either.right({
content: response.message.content || '',
role: response.message.role || 'assistant',
model: response.model || data.model,
done: response.done ?? true,
total_duration: response.total_duration,
eval_count: response.eval_count,
});
}
else if (response.error) {
return Either.left(`Ollama error: ${response.error}`);
}
else {
return Either.left(`Unexpected Ollama chat response format: ${JSON.stringify(response)}`);
}
}
async validate_connection() {
const result = await this._make_request(this.config.models_path, null, 'GET');
return result.isRight;
}
async list_models() {
const result = await this._make_request(this.config.models_path, null, 'GET');
if (result.isLeft) {
return Either.left(result.left);
}
const response = result.right;
if (response.models) {
const models = response.models.map((m) => m.name || m.model || 'unknown');
return Either.right(models);
}
else {
return Either.left(`Unexpected models response: ${JSON.stringify(response)}`);
}
}
}
//# sourceMappingURL=OllamaProvider.js.map