2208 lines
75 KiB
JavaScript
2208 lines
75 KiB
JavaScript
/*
|
|
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
|
if you want to view the source, please visit the github repository of this plugin
|
|
*/
|
|
|
|
var __create = Object.create;
|
|
var __defProp = Object.defineProperty;
|
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
var __getProtoOf = Object.getPrototypeOf;
|
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
var __typeError = (msg) => {
|
|
throw TypeError(msg);
|
|
};
|
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
var __commonJS = (cb, mod) => function __require() {
|
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
};
|
|
var __export = (target, all) => {
|
|
for (var name in all)
|
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
};
|
|
var __copyProps = (to, from, except, desc) => {
|
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
for (let key of __getOwnPropNames(from))
|
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
}
|
|
return to;
|
|
};
|
|
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
// If the importer is in node compatibility mode or this is not an ESM
|
|
// file that has been converted to a CommonJS file using a Babel-
|
|
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
mod
|
|
));
|
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
var __privateWrapper = (obj, member, setter, getter) => ({
|
|
set _(value) {
|
|
__privateSet(obj, member, value, setter);
|
|
},
|
|
get _() {
|
|
return __privateGet(obj, member, getter);
|
|
}
|
|
});
|
|
|
|
// node_modules/eventemitter3/index.js
|
|
var require_eventemitter3 = __commonJS({
|
|
"node_modules/eventemitter3/index.js"(exports, module2) {
|
|
"use strict";
|
|
var has = Object.prototype.hasOwnProperty;
|
|
var prefix = "~";
|
|
function Events() {
|
|
}
|
|
if (Object.create) {
|
|
Events.prototype = /* @__PURE__ */ Object.create(null);
|
|
if (!new Events().__proto__) prefix = false;
|
|
}
|
|
function EE(fn, context, once) {
|
|
this.fn = fn;
|
|
this.context = context;
|
|
this.once = once || false;
|
|
}
|
|
function addListener(emitter, event, fn, context, once) {
|
|
if (typeof fn !== "function") {
|
|
throw new TypeError("The listener must be a function");
|
|
}
|
|
var listener = new EE(fn, context || emitter, once), evt = prefix ? prefix + event : event;
|
|
if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;
|
|
else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);
|
|
else emitter._events[evt] = [emitter._events[evt], listener];
|
|
return emitter;
|
|
}
|
|
function clearEvent(emitter, evt) {
|
|
if (--emitter._eventsCount === 0) emitter._events = new Events();
|
|
else delete emitter._events[evt];
|
|
}
|
|
function EventEmitter2() {
|
|
this._events = new Events();
|
|
this._eventsCount = 0;
|
|
}
|
|
EventEmitter2.prototype.eventNames = function eventNames() {
|
|
var names = [], events, name;
|
|
if (this._eventsCount === 0) return names;
|
|
for (name in events = this._events) {
|
|
if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);
|
|
}
|
|
if (Object.getOwnPropertySymbols) {
|
|
return names.concat(Object.getOwnPropertySymbols(events));
|
|
}
|
|
return names;
|
|
};
|
|
EventEmitter2.prototype.listeners = function listeners(event) {
|
|
var evt = prefix ? prefix + event : event, handlers = this._events[evt];
|
|
if (!handlers) return [];
|
|
if (handlers.fn) return [handlers.fn];
|
|
for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
|
|
ee[i] = handlers[i].fn;
|
|
}
|
|
return ee;
|
|
};
|
|
EventEmitter2.prototype.listenerCount = function listenerCount(event) {
|
|
var evt = prefix ? prefix + event : event, listeners = this._events[evt];
|
|
if (!listeners) return 0;
|
|
if (listeners.fn) return 1;
|
|
return listeners.length;
|
|
};
|
|
EventEmitter2.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
|
|
var evt = prefix ? prefix + event : event;
|
|
if (!this._events[evt]) return false;
|
|
var listeners = this._events[evt], len = arguments.length, args, i;
|
|
if (listeners.fn) {
|
|
if (listeners.once) this.removeListener(event, listeners.fn, void 0, true);
|
|
switch (len) {
|
|
case 1:
|
|
return listeners.fn.call(listeners.context), true;
|
|
case 2:
|
|
return listeners.fn.call(listeners.context, a1), true;
|
|
case 3:
|
|
return listeners.fn.call(listeners.context, a1, a2), true;
|
|
case 4:
|
|
return listeners.fn.call(listeners.context, a1, a2, a3), true;
|
|
case 5:
|
|
return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
|
|
case 6:
|
|
return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
|
|
}
|
|
for (i = 1, args = new Array(len - 1); i < len; i++) {
|
|
args[i - 1] = arguments[i];
|
|
}
|
|
listeners.fn.apply(listeners.context, args);
|
|
} else {
|
|
var length = listeners.length, j;
|
|
for (i = 0; i < length; i++) {
|
|
if (listeners[i].once) this.removeListener(event, listeners[i].fn, void 0, true);
|
|
switch (len) {
|
|
case 1:
|
|
listeners[i].fn.call(listeners[i].context);
|
|
break;
|
|
case 2:
|
|
listeners[i].fn.call(listeners[i].context, a1);
|
|
break;
|
|
case 3:
|
|
listeners[i].fn.call(listeners[i].context, a1, a2);
|
|
break;
|
|
case 4:
|
|
listeners[i].fn.call(listeners[i].context, a1, a2, a3);
|
|
break;
|
|
default:
|
|
if (!args) for (j = 1, args = new Array(len - 1); j < len; j++) {
|
|
args[j - 1] = arguments[j];
|
|
}
|
|
listeners[i].fn.apply(listeners[i].context, args);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
EventEmitter2.prototype.on = function on(event, fn, context) {
|
|
return addListener(this, event, fn, context, false);
|
|
};
|
|
EventEmitter2.prototype.once = function once(event, fn, context) {
|
|
return addListener(this, event, fn, context, true);
|
|
};
|
|
EventEmitter2.prototype.removeListener = function removeListener(event, fn, context, once) {
|
|
var evt = prefix ? prefix + event : event;
|
|
if (!this._events[evt]) return this;
|
|
if (!fn) {
|
|
clearEvent(this, evt);
|
|
return this;
|
|
}
|
|
var listeners = this._events[evt];
|
|
if (listeners.fn) {
|
|
if (listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context)) {
|
|
clearEvent(this, evt);
|
|
}
|
|
} else {
|
|
for (var i = 0, events = [], length = listeners.length; i < length; i++) {
|
|
if (listeners[i].fn !== fn || once && !listeners[i].once || context && listeners[i].context !== context) {
|
|
events.push(listeners[i]);
|
|
}
|
|
}
|
|
if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;
|
|
else clearEvent(this, evt);
|
|
}
|
|
return this;
|
|
};
|
|
EventEmitter2.prototype.removeAllListeners = function removeAllListeners(event) {
|
|
var evt;
|
|
if (event) {
|
|
evt = prefix ? prefix + event : event;
|
|
if (this._events[evt]) clearEvent(this, evt);
|
|
} else {
|
|
this._events = new Events();
|
|
this._eventsCount = 0;
|
|
}
|
|
return this;
|
|
};
|
|
EventEmitter2.prototype.off = EventEmitter2.prototype.removeListener;
|
|
EventEmitter2.prototype.addListener = EventEmitter2.prototype.on;
|
|
EventEmitter2.prefixed = prefix;
|
|
EventEmitter2.EventEmitter = EventEmitter2;
|
|
if ("undefined" !== typeof module2) {
|
|
module2.exports = EventEmitter2;
|
|
}
|
|
}
|
|
});
|
|
|
|
// node_modules/canvas/lib/parse-font.js
|
|
var require_parse_font = __commonJS({
|
|
"node_modules/canvas/lib/parse-font.js"(exports, module2) {
|
|
"use strict";
|
|
var weights = "bold|bolder|lighter|[1-9]00";
|
|
var styles = "italic|oblique";
|
|
var variants = "small-caps";
|
|
var stretches = "ultra-condensed|extra-condensed|condensed|semi-condensed|semi-expanded|expanded|extra-expanded|ultra-expanded";
|
|
var units = "px|pt|pc|in|cm|mm|%|em|ex|ch|rem|q";
|
|
var string = /'((\\'|[^'])+)'|"((\\"|[^"])+)"|[\w\s-]+/.source;
|
|
var weightRe = new RegExp(`(${weights}) +`, "i");
|
|
var styleRe = new RegExp(`(${styles}) +`, "i");
|
|
var variantRe = new RegExp(`(${variants}) +`, "i");
|
|
var stretchRe = new RegExp(`(${stretches}) +`, "i");
|
|
var familyRe = new RegExp(string, "g");
|
|
var unquoteRe = /^['"](.*)['"]$/;
|
|
var unescapeRe = /\\(['"])/g;
|
|
var sizeFamilyRe = new RegExp(
|
|
`([\\d\\.]+)(${units}) *((?:${string})( *, *(?:${string}))*)`
|
|
);
|
|
var cache = {};
|
|
var defaultHeight = 16;
|
|
module2.exports = (str) => {
|
|
if (cache[str]) return cache[str];
|
|
const sizeFamily = sizeFamilyRe.exec(str);
|
|
if (!sizeFamily) return;
|
|
const names = sizeFamily[3].match(familyRe).map((s) => s.trim().replace(unquoteRe, "$1").replace(unescapeRe, "$1")).filter((s) => !!s);
|
|
const font = {
|
|
weight: "normal",
|
|
style: "normal",
|
|
stretch: "normal",
|
|
variant: "normal",
|
|
size: parseFloat(sizeFamily[1]),
|
|
unit: sizeFamily[2],
|
|
family: names.join(",")
|
|
};
|
|
let weight, style, variant, stretch;
|
|
const substr = str.substring(0, sizeFamily.index);
|
|
if (weight = weightRe.exec(substr)) font.weight = weight[1];
|
|
if (style = styleRe.exec(substr)) font.style = style[1];
|
|
if (variant = variantRe.exec(substr)) font.variant = variant[1];
|
|
if (stretch = stretchRe.exec(substr)) font.stretch = stretch[1];
|
|
switch (font.unit) {
|
|
case "pt":
|
|
font.size /= 0.75;
|
|
break;
|
|
case "pc":
|
|
font.size *= 16;
|
|
break;
|
|
case "in":
|
|
font.size *= 96;
|
|
break;
|
|
case "cm":
|
|
font.size *= 96 / 2.54;
|
|
break;
|
|
case "mm":
|
|
font.size *= 96 / 25.4;
|
|
break;
|
|
case "%":
|
|
break;
|
|
case "em":
|
|
case "rem":
|
|
font.size *= defaultHeight / 0.75;
|
|
break;
|
|
case "q":
|
|
font.size *= 96 / 25.4 / 4;
|
|
break;
|
|
}
|
|
return cache[str] = font;
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/canvas/browser.js
|
|
var require_browser = __commonJS({
|
|
"node_modules/canvas/browser.js"(exports) {
|
|
var parseFont = require_parse_font();
|
|
exports.parseFont = parseFont;
|
|
exports.createCanvas = function(width, height) {
|
|
return Object.assign(document.createElement("canvas"), { width, height });
|
|
};
|
|
exports.createImageData = function(array, width, height) {
|
|
switch (arguments.length) {
|
|
case 0:
|
|
return new ImageData();
|
|
case 1:
|
|
return new ImageData(array);
|
|
case 2:
|
|
return new ImageData(array, width);
|
|
default:
|
|
return new ImageData(array, width, height);
|
|
}
|
|
};
|
|
exports.loadImage = function(src, options) {
|
|
return new Promise(function(resolve2, reject) {
|
|
const image = Object.assign(document.createElement("img"), options);
|
|
function cleanup() {
|
|
image.onload = null;
|
|
image.onerror = null;
|
|
}
|
|
image.onload = function() {
|
|
cleanup();
|
|
resolve2(image);
|
|
};
|
|
image.onerror = function() {
|
|
cleanup();
|
|
reject(new Error('Failed to load the image "' + src + '"'));
|
|
};
|
|
image.src = src;
|
|
});
|
|
};
|
|
}
|
|
});
|
|
|
|
// src/main.ts
|
|
var main_exports = {};
|
|
__export(main_exports, {
|
|
default: () => AIImageAnalyzerPlugin
|
|
});
|
|
module.exports = __toCommonJS(main_exports);
|
|
var import_obsidian4 = require("obsidian");
|
|
|
|
// node_modules/ollama/dist/index.mjs
|
|
var import_fs = __toESM(require("fs"), 1);
|
|
var import_path = require("path");
|
|
|
|
// node_modules/whatwg-fetch/fetch.js
|
|
var g = typeof globalThis !== "undefined" && globalThis || typeof self !== "undefined" && self || // eslint-disable-next-line no-undef
|
|
typeof global !== "undefined" && global || {};
|
|
var support = {
|
|
searchParams: "URLSearchParams" in g,
|
|
iterable: "Symbol" in g && "iterator" in Symbol,
|
|
blob: "FileReader" in g && "Blob" in g && function() {
|
|
try {
|
|
new Blob();
|
|
return true;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}(),
|
|
formData: "FormData" in g,
|
|
arrayBuffer: "ArrayBuffer" in g
|
|
};
|
|
function isDataView(obj) {
|
|
return obj && DataView.prototype.isPrototypeOf(obj);
|
|
}
|
|
if (support.arrayBuffer) {
|
|
viewClasses = [
|
|
"[object Int8Array]",
|
|
"[object Uint8Array]",
|
|
"[object Uint8ClampedArray]",
|
|
"[object Int16Array]",
|
|
"[object Uint16Array]",
|
|
"[object Int32Array]",
|
|
"[object Uint32Array]",
|
|
"[object Float32Array]",
|
|
"[object Float64Array]"
|
|
];
|
|
isArrayBufferView = ArrayBuffer.isView || function(obj) {
|
|
return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1;
|
|
};
|
|
}
|
|
var viewClasses;
|
|
var isArrayBufferView;
|
|
function normalizeName(name) {
|
|
if (typeof name !== "string") {
|
|
name = String(name);
|
|
}
|
|
if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === "") {
|
|
throw new TypeError('Invalid character in header field name: "' + name + '"');
|
|
}
|
|
return name.toLowerCase();
|
|
}
|
|
function normalizeValue(value) {
|
|
if (typeof value !== "string") {
|
|
value = String(value);
|
|
}
|
|
return value;
|
|
}
|
|
function iteratorFor(items) {
|
|
var iterator = {
|
|
next: function() {
|
|
var value = items.shift();
|
|
return { done: value === void 0, value };
|
|
}
|
|
};
|
|
if (support.iterable) {
|
|
iterator[Symbol.iterator] = function() {
|
|
return iterator;
|
|
};
|
|
}
|
|
return iterator;
|
|
}
|
|
function Headers(headers) {
|
|
this.map = {};
|
|
if (headers instanceof Headers) {
|
|
headers.forEach(function(value, name) {
|
|
this.append(name, value);
|
|
}, this);
|
|
} else if (Array.isArray(headers)) {
|
|
headers.forEach(function(header) {
|
|
if (header.length != 2) {
|
|
throw new TypeError("Headers constructor: expected name/value pair to be length 2, found" + header.length);
|
|
}
|
|
this.append(header[0], header[1]);
|
|
}, this);
|
|
} else if (headers) {
|
|
Object.getOwnPropertyNames(headers).forEach(function(name) {
|
|
this.append(name, headers[name]);
|
|
}, this);
|
|
}
|
|
}
|
|
Headers.prototype.append = function(name, value) {
|
|
name = normalizeName(name);
|
|
value = normalizeValue(value);
|
|
var oldValue = this.map[name];
|
|
this.map[name] = oldValue ? oldValue + ", " + value : value;
|
|
};
|
|
Headers.prototype["delete"] = function(name) {
|
|
delete this.map[normalizeName(name)];
|
|
};
|
|
Headers.prototype.get = function(name) {
|
|
name = normalizeName(name);
|
|
return this.has(name) ? this.map[name] : null;
|
|
};
|
|
Headers.prototype.has = function(name) {
|
|
return this.map.hasOwnProperty(normalizeName(name));
|
|
};
|
|
Headers.prototype.set = function(name, value) {
|
|
this.map[normalizeName(name)] = normalizeValue(value);
|
|
};
|
|
Headers.prototype.forEach = function(callback, thisArg) {
|
|
for (var name in this.map) {
|
|
if (this.map.hasOwnProperty(name)) {
|
|
callback.call(thisArg, this.map[name], name, this);
|
|
}
|
|
}
|
|
};
|
|
Headers.prototype.keys = function() {
|
|
var items = [];
|
|
this.forEach(function(value, name) {
|
|
items.push(name);
|
|
});
|
|
return iteratorFor(items);
|
|
};
|
|
Headers.prototype.values = function() {
|
|
var items = [];
|
|
this.forEach(function(value) {
|
|
items.push(value);
|
|
});
|
|
return iteratorFor(items);
|
|
};
|
|
Headers.prototype.entries = function() {
|
|
var items = [];
|
|
this.forEach(function(value, name) {
|
|
items.push([name, value]);
|
|
});
|
|
return iteratorFor(items);
|
|
};
|
|
if (support.iterable) {
|
|
Headers.prototype[Symbol.iterator] = Headers.prototype.entries;
|
|
}
|
|
function consumed(body) {
|
|
if (body._noBody) return;
|
|
if (body.bodyUsed) {
|
|
return Promise.reject(new TypeError("Already read"));
|
|
}
|
|
body.bodyUsed = true;
|
|
}
|
|
function fileReaderReady(reader) {
|
|
return new Promise(function(resolve2, reject) {
|
|
reader.onload = function() {
|
|
resolve2(reader.result);
|
|
};
|
|
reader.onerror = function() {
|
|
reject(reader.error);
|
|
};
|
|
});
|
|
}
|
|
function readBlobAsArrayBuffer(blob) {
|
|
var reader = new FileReader();
|
|
var promise = fileReaderReady(reader);
|
|
reader.readAsArrayBuffer(blob);
|
|
return promise;
|
|
}
|
|
function readBlobAsText(blob) {
|
|
var reader = new FileReader();
|
|
var promise = fileReaderReady(reader);
|
|
var match = /charset=([A-Za-z0-9_-]+)/.exec(blob.type);
|
|
var encoding = match ? match[1] : "utf-8";
|
|
reader.readAsText(blob, encoding);
|
|
return promise;
|
|
}
|
|
function readArrayBufferAsText(buf) {
|
|
var view = new Uint8Array(buf);
|
|
var chars = new Array(view.length);
|
|
for (var i = 0; i < view.length; i++) {
|
|
chars[i] = String.fromCharCode(view[i]);
|
|
}
|
|
return chars.join("");
|
|
}
|
|
function bufferClone(buf) {
|
|
if (buf.slice) {
|
|
return buf.slice(0);
|
|
} else {
|
|
var view = new Uint8Array(buf.byteLength);
|
|
view.set(new Uint8Array(buf));
|
|
return view.buffer;
|
|
}
|
|
}
|
|
function Body() {
|
|
this.bodyUsed = false;
|
|
this._initBody = function(body) {
|
|
this.bodyUsed = this.bodyUsed;
|
|
this._bodyInit = body;
|
|
if (!body) {
|
|
this._noBody = true;
|
|
this._bodyText = "";
|
|
} else if (typeof body === "string") {
|
|
this._bodyText = body;
|
|
} else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
|
|
this._bodyBlob = body;
|
|
} else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
|
|
this._bodyFormData = body;
|
|
} else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
|
|
this._bodyText = body.toString();
|
|
} else if (support.arrayBuffer && support.blob && isDataView(body)) {
|
|
this._bodyArrayBuffer = bufferClone(body.buffer);
|
|
this._bodyInit = new Blob([this._bodyArrayBuffer]);
|
|
} else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
|
|
this._bodyArrayBuffer = bufferClone(body);
|
|
} else {
|
|
this._bodyText = body = Object.prototype.toString.call(body);
|
|
}
|
|
if (!this.headers.get("content-type")) {
|
|
if (typeof body === "string") {
|
|
this.headers.set("content-type", "text/plain;charset=UTF-8");
|
|
} else if (this._bodyBlob && this._bodyBlob.type) {
|
|
this.headers.set("content-type", this._bodyBlob.type);
|
|
} else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
|
|
this.headers.set("content-type", "application/x-www-form-urlencoded;charset=UTF-8");
|
|
}
|
|
}
|
|
};
|
|
if (support.blob) {
|
|
this.blob = function() {
|
|
var rejected = consumed(this);
|
|
if (rejected) {
|
|
return rejected;
|
|
}
|
|
if (this._bodyBlob) {
|
|
return Promise.resolve(this._bodyBlob);
|
|
} else if (this._bodyArrayBuffer) {
|
|
return Promise.resolve(new Blob([this._bodyArrayBuffer]));
|
|
} else if (this._bodyFormData) {
|
|
throw new Error("could not read FormData body as blob");
|
|
} else {
|
|
return Promise.resolve(new Blob([this._bodyText]));
|
|
}
|
|
};
|
|
}
|
|
this.arrayBuffer = function() {
|
|
if (this._bodyArrayBuffer) {
|
|
var isConsumed = consumed(this);
|
|
if (isConsumed) {
|
|
return isConsumed;
|
|
} else if (ArrayBuffer.isView(this._bodyArrayBuffer)) {
|
|
return Promise.resolve(
|
|
this._bodyArrayBuffer.buffer.slice(
|
|
this._bodyArrayBuffer.byteOffset,
|
|
this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength
|
|
)
|
|
);
|
|
} else {
|
|
return Promise.resolve(this._bodyArrayBuffer);
|
|
}
|
|
} else if (support.blob) {
|
|
return this.blob().then(readBlobAsArrayBuffer);
|
|
} else {
|
|
throw new Error("could not read as ArrayBuffer");
|
|
}
|
|
};
|
|
this.text = function() {
|
|
var rejected = consumed(this);
|
|
if (rejected) {
|
|
return rejected;
|
|
}
|
|
if (this._bodyBlob) {
|
|
return readBlobAsText(this._bodyBlob);
|
|
} else if (this._bodyArrayBuffer) {
|
|
return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer));
|
|
} else if (this._bodyFormData) {
|
|
throw new Error("could not read FormData body as text");
|
|
} else {
|
|
return Promise.resolve(this._bodyText);
|
|
}
|
|
};
|
|
if (support.formData) {
|
|
this.formData = function() {
|
|
return this.text().then(decode);
|
|
};
|
|
}
|
|
this.json = function() {
|
|
return this.text().then(JSON.parse);
|
|
};
|
|
return this;
|
|
}
|
|
var methods = ["CONNECT", "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "TRACE"];
|
|
function normalizeMethod(method) {
|
|
var upcased = method.toUpperCase();
|
|
return methods.indexOf(upcased) > -1 ? upcased : method;
|
|
}
|
|
function Request(input, options) {
|
|
if (!(this instanceof Request)) {
|
|
throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');
|
|
}
|
|
options = options || {};
|
|
var body = options.body;
|
|
if (input instanceof Request) {
|
|
if (input.bodyUsed) {
|
|
throw new TypeError("Already read");
|
|
}
|
|
this.url = input.url;
|
|
this.credentials = input.credentials;
|
|
if (!options.headers) {
|
|
this.headers = new Headers(input.headers);
|
|
}
|
|
this.method = input.method;
|
|
this.mode = input.mode;
|
|
this.signal = input.signal;
|
|
if (!body && input._bodyInit != null) {
|
|
body = input._bodyInit;
|
|
input.bodyUsed = true;
|
|
}
|
|
} else {
|
|
this.url = String(input);
|
|
}
|
|
this.credentials = options.credentials || this.credentials || "same-origin";
|
|
if (options.headers || !this.headers) {
|
|
this.headers = new Headers(options.headers);
|
|
}
|
|
this.method = normalizeMethod(options.method || this.method || "GET");
|
|
this.mode = options.mode || this.mode || null;
|
|
this.signal = options.signal || this.signal || function() {
|
|
if ("AbortController" in g) {
|
|
var ctrl = new AbortController();
|
|
return ctrl.signal;
|
|
}
|
|
}();
|
|
this.referrer = null;
|
|
if ((this.method === "GET" || this.method === "HEAD") && body) {
|
|
throw new TypeError("Body not allowed for GET or HEAD requests");
|
|
}
|
|
this._initBody(body);
|
|
if (this.method === "GET" || this.method === "HEAD") {
|
|
if (options.cache === "no-store" || options.cache === "no-cache") {
|
|
var reParamSearch = /([?&])_=[^&]*/;
|
|
if (reParamSearch.test(this.url)) {
|
|
this.url = this.url.replace(reParamSearch, "$1_=" + (/* @__PURE__ */ new Date()).getTime());
|
|
} else {
|
|
var reQueryString = /\?/;
|
|
this.url += (reQueryString.test(this.url) ? "&" : "?") + "_=" + (/* @__PURE__ */ new Date()).getTime();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Request.prototype.clone = function() {
|
|
return new Request(this, { body: this._bodyInit });
|
|
};
|
|
function decode(body) {
|
|
var form = new FormData();
|
|
body.trim().split("&").forEach(function(bytes) {
|
|
if (bytes) {
|
|
var split = bytes.split("=");
|
|
var name = split.shift().replace(/\+/g, " ");
|
|
var value = split.join("=").replace(/\+/g, " ");
|
|
form.append(decodeURIComponent(name), decodeURIComponent(value));
|
|
}
|
|
});
|
|
return form;
|
|
}
|
|
function parseHeaders(rawHeaders) {
|
|
var headers = new Headers();
|
|
var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, " ");
|
|
preProcessedHeaders.split("\r").map(function(header) {
|
|
return header.indexOf("\n") === 0 ? header.substr(1, header.length) : header;
|
|
}).forEach(function(line) {
|
|
var parts = line.split(":");
|
|
var key = parts.shift().trim();
|
|
if (key) {
|
|
var value = parts.join(":").trim();
|
|
try {
|
|
headers.append(key, value);
|
|
} catch (error) {
|
|
console.warn("Response " + error.message);
|
|
}
|
|
}
|
|
});
|
|
return headers;
|
|
}
|
|
Body.call(Request.prototype);
|
|
function Response(bodyInit, options) {
|
|
if (!(this instanceof Response)) {
|
|
throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');
|
|
}
|
|
if (!options) {
|
|
options = {};
|
|
}
|
|
this.type = "default";
|
|
this.status = options.status === void 0 ? 200 : options.status;
|
|
if (this.status < 200 || this.status > 599) {
|
|
throw new RangeError("Failed to construct 'Response': The status provided (0) is outside the range [200, 599].");
|
|
}
|
|
this.ok = this.status >= 200 && this.status < 300;
|
|
this.statusText = options.statusText === void 0 ? "" : "" + options.statusText;
|
|
this.headers = new Headers(options.headers);
|
|
this.url = options.url || "";
|
|
this._initBody(bodyInit);
|
|
}
|
|
Body.call(Response.prototype);
|
|
Response.prototype.clone = function() {
|
|
return new Response(this._bodyInit, {
|
|
status: this.status,
|
|
statusText: this.statusText,
|
|
headers: new Headers(this.headers),
|
|
url: this.url
|
|
});
|
|
};
|
|
Response.error = function() {
|
|
var response = new Response(null, { status: 200, statusText: "" });
|
|
response.ok = false;
|
|
response.status = 0;
|
|
response.type = "error";
|
|
return response;
|
|
};
|
|
var redirectStatuses = [301, 302, 303, 307, 308];
|
|
Response.redirect = function(url, status) {
|
|
if (redirectStatuses.indexOf(status) === -1) {
|
|
throw new RangeError("Invalid status code");
|
|
}
|
|
return new Response(null, { status, headers: { location: url } });
|
|
};
|
|
var DOMException2 = g.DOMException;
|
|
try {
|
|
new DOMException2();
|
|
} catch (err) {
|
|
DOMException2 = function(message, name) {
|
|
this.message = message;
|
|
this.name = name;
|
|
var error = Error(message);
|
|
this.stack = error.stack;
|
|
};
|
|
DOMException2.prototype = Object.create(Error.prototype);
|
|
DOMException2.prototype.constructor = DOMException2;
|
|
}
|
|
function fetch2(input, init) {
|
|
return new Promise(function(resolve2, reject) {
|
|
var request = new Request(input, init);
|
|
if (request.signal && request.signal.aborted) {
|
|
return reject(new DOMException2("Aborted", "AbortError"));
|
|
}
|
|
var xhr = new XMLHttpRequest();
|
|
function abortXhr() {
|
|
xhr.abort();
|
|
}
|
|
xhr.onload = function() {
|
|
var options = {
|
|
statusText: xhr.statusText,
|
|
headers: parseHeaders(xhr.getAllResponseHeaders() || "")
|
|
};
|
|
if (request.url.indexOf("file://") === 0 && (xhr.status < 200 || xhr.status > 599)) {
|
|
options.status = 200;
|
|
} else {
|
|
options.status = xhr.status;
|
|
}
|
|
options.url = "responseURL" in xhr ? xhr.responseURL : options.headers.get("X-Request-URL");
|
|
var body = "response" in xhr ? xhr.response : xhr.responseText;
|
|
setTimeout(function() {
|
|
resolve2(new Response(body, options));
|
|
}, 0);
|
|
};
|
|
xhr.onerror = function() {
|
|
setTimeout(function() {
|
|
reject(new TypeError("Network request failed"));
|
|
}, 0);
|
|
};
|
|
xhr.ontimeout = function() {
|
|
setTimeout(function() {
|
|
reject(new TypeError("Network request timed out"));
|
|
}, 0);
|
|
};
|
|
xhr.onabort = function() {
|
|
setTimeout(function() {
|
|
reject(new DOMException2("Aborted", "AbortError"));
|
|
}, 0);
|
|
};
|
|
function fixUrl(url) {
|
|
try {
|
|
return url === "" && g.location.href ? g.location.href : url;
|
|
} catch (e) {
|
|
return url;
|
|
}
|
|
}
|
|
xhr.open(request.method, fixUrl(request.url), true);
|
|
if (request.credentials === "include") {
|
|
xhr.withCredentials = true;
|
|
} else if (request.credentials === "omit") {
|
|
xhr.withCredentials = false;
|
|
}
|
|
if ("responseType" in xhr) {
|
|
if (support.blob) {
|
|
xhr.responseType = "blob";
|
|
} else if (support.arrayBuffer) {
|
|
xhr.responseType = "arraybuffer";
|
|
}
|
|
}
|
|
if (init && typeof init.headers === "object" && !(init.headers instanceof Headers || g.Headers && init.headers instanceof g.Headers)) {
|
|
var names = [];
|
|
Object.getOwnPropertyNames(init.headers).forEach(function(name) {
|
|
names.push(normalizeName(name));
|
|
xhr.setRequestHeader(name, normalizeValue(init.headers[name]));
|
|
});
|
|
request.headers.forEach(function(value, name) {
|
|
if (names.indexOf(name) === -1) {
|
|
xhr.setRequestHeader(name, value);
|
|
}
|
|
});
|
|
} else {
|
|
request.headers.forEach(function(value, name) {
|
|
xhr.setRequestHeader(name, value);
|
|
});
|
|
}
|
|
if (request.signal) {
|
|
request.signal.addEventListener("abort", abortXhr);
|
|
xhr.onreadystatechange = function() {
|
|
if (xhr.readyState === 4) {
|
|
request.signal.removeEventListener("abort", abortXhr);
|
|
}
|
|
};
|
|
}
|
|
xhr.send(typeof request._bodyInit === "undefined" ? null : request._bodyInit);
|
|
});
|
|
}
|
|
fetch2.polyfill = true;
|
|
if (!g.fetch) {
|
|
g.fetch = fetch2;
|
|
g.Headers = Headers;
|
|
g.Request = Request;
|
|
g.Response = Response;
|
|
}
|
|
|
|
// node_modules/ollama/dist/browser.mjs
|
|
var version = "0.5.12";
|
|
var __defProp$1 = Object.defineProperty;
|
|
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
var __publicField$1 = (obj, key, value) => {
|
|
__defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
return value;
|
|
};
|
|
var ResponseError = class _ResponseError extends Error {
|
|
constructor(error, status_code) {
|
|
super(error);
|
|
this.error = error;
|
|
this.status_code = status_code;
|
|
this.name = "ResponseError";
|
|
if (Error.captureStackTrace) {
|
|
Error.captureStackTrace(this, _ResponseError);
|
|
}
|
|
}
|
|
};
|
|
var AbortableAsyncIterator = class {
|
|
constructor(abortController, itr, doneCallback) {
|
|
__publicField$1(this, "abortController");
|
|
__publicField$1(this, "itr");
|
|
__publicField$1(this, "doneCallback");
|
|
this.abortController = abortController;
|
|
this.itr = itr;
|
|
this.doneCallback = doneCallback;
|
|
}
|
|
abort() {
|
|
this.abortController.abort();
|
|
}
|
|
async *[Symbol.asyncIterator]() {
|
|
for await (const message of this.itr) {
|
|
if ("error" in message) {
|
|
throw new Error(message.error);
|
|
}
|
|
yield message;
|
|
if (message.done || message.status === "success") {
|
|
this.doneCallback();
|
|
return;
|
|
}
|
|
}
|
|
throw new Error("Did not receive done or success response in stream.");
|
|
}
|
|
};
|
|
var checkOk = async (response) => {
|
|
var _a;
|
|
if (response.ok) {
|
|
return;
|
|
}
|
|
let message = `Error ${response.status}: ${response.statusText}`;
|
|
let errorData = null;
|
|
if ((_a = response.headers.get("content-type")) == null ? void 0 : _a.includes("application/json")) {
|
|
try {
|
|
errorData = await response.json();
|
|
message = errorData.error || message;
|
|
} catch (error) {
|
|
console.log("Failed to parse error response as JSON");
|
|
}
|
|
} else {
|
|
try {
|
|
console.log("Getting text from response");
|
|
const textResponse = await response.text();
|
|
message = textResponse || message;
|
|
} catch (error) {
|
|
console.log("Failed to get text from error response");
|
|
}
|
|
}
|
|
throw new ResponseError(message, response.status);
|
|
};
|
|
function getPlatform() {
|
|
if (typeof window !== "undefined" && window.navigator) {
|
|
return `${window.navigator.platform.toLowerCase()} Browser/${navigator.userAgent};`;
|
|
} else if (typeof process !== "undefined") {
|
|
return `${process.arch} ${process.platform} Node.js/${process.version}`;
|
|
}
|
|
return "";
|
|
}
|
|
var fetchWithHeaders = async (fetch3, url, options = {}) => {
|
|
const defaultHeaders = {
|
|
"Content-Type": "application/json",
|
|
Accept: "application/json",
|
|
"User-Agent": `ollama-js/${version} (${getPlatform()})`
|
|
};
|
|
if (!options.headers) {
|
|
options.headers = {};
|
|
}
|
|
const customHeaders = Object.fromEntries(
|
|
Object.entries(options.headers).filter(([key]) => !Object.keys(defaultHeaders).some((defaultKey) => defaultKey.toLowerCase() === key.toLowerCase()))
|
|
);
|
|
options.headers = {
|
|
...defaultHeaders,
|
|
...customHeaders
|
|
};
|
|
return fetch3(url, options);
|
|
};
|
|
var get = async (fetch3, host, options) => {
|
|
const response = await fetchWithHeaders(fetch3, host, {
|
|
headers: options == null ? void 0 : options.headers
|
|
});
|
|
await checkOk(response);
|
|
return response;
|
|
};
|
|
var post = async (fetch3, host, data, options) => {
|
|
const isRecord = (input) => {
|
|
return input !== null && typeof input === "object" && !Array.isArray(input);
|
|
};
|
|
const formattedData = isRecord(data) ? JSON.stringify(data) : data;
|
|
const response = await fetchWithHeaders(fetch3, host, {
|
|
method: "POST",
|
|
body: formattedData,
|
|
signal: options == null ? void 0 : options.signal,
|
|
headers: options == null ? void 0 : options.headers
|
|
});
|
|
await checkOk(response);
|
|
return response;
|
|
};
|
|
var del = async (fetch3, host, data, options) => {
|
|
const response = await fetchWithHeaders(fetch3, host, {
|
|
method: "DELETE",
|
|
body: JSON.stringify(data),
|
|
headers: options == null ? void 0 : options.headers
|
|
});
|
|
await checkOk(response);
|
|
return response;
|
|
};
|
|
var parseJSON = async function* (itr) {
|
|
var _a;
|
|
const decoder = new TextDecoder("utf-8");
|
|
let buffer = "";
|
|
const reader = itr.getReader();
|
|
while (true) {
|
|
const { done, value: chunk } = await reader.read();
|
|
if (done) {
|
|
break;
|
|
}
|
|
buffer += decoder.decode(chunk);
|
|
const parts = buffer.split("\n");
|
|
buffer = (_a = parts.pop()) != null ? _a : "";
|
|
for (const part of parts) {
|
|
try {
|
|
yield JSON.parse(part);
|
|
} catch (error) {
|
|
console.warn("invalid json: ", part);
|
|
}
|
|
}
|
|
}
|
|
for (const part of buffer.split("\n").filter((p) => p !== "")) {
|
|
try {
|
|
yield JSON.parse(part);
|
|
} catch (error) {
|
|
console.warn("invalid json: ", part);
|
|
}
|
|
}
|
|
};
|
|
var formatHost = (host) => {
|
|
if (!host) {
|
|
return "http://127.0.0.1:11434";
|
|
}
|
|
let isExplicitProtocol = host.includes("://");
|
|
if (host.startsWith(":")) {
|
|
host = `http://127.0.0.1${host}`;
|
|
isExplicitProtocol = true;
|
|
}
|
|
if (!isExplicitProtocol) {
|
|
host = `http://${host}`;
|
|
}
|
|
const url = new URL(host);
|
|
let port = url.port;
|
|
if (!port) {
|
|
if (!isExplicitProtocol) {
|
|
port = "11434";
|
|
} else {
|
|
port = url.protocol === "https:" ? "443" : "80";
|
|
}
|
|
}
|
|
let formattedHost = `${url.protocol}//${url.hostname}:${port}${url.pathname}`;
|
|
if (formattedHost.endsWith("/")) {
|
|
formattedHost = formattedHost.slice(0, -1);
|
|
}
|
|
return formattedHost;
|
|
};
|
|
var __defProp2 = Object.defineProperty;
|
|
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
var __publicField2 = (obj, key, value) => {
|
|
__defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
return value;
|
|
};
|
|
var Ollama$1 = class Ollama {
|
|
constructor(config) {
|
|
var _a, _b;
|
|
__publicField2(this, "config");
|
|
__publicField2(this, "fetch");
|
|
__publicField2(this, "ongoingStreamedRequests", []);
|
|
this.config = {
|
|
host: "",
|
|
headers: config == null ? void 0 : config.headers
|
|
};
|
|
if (!(config == null ? void 0 : config.proxy)) {
|
|
this.config.host = formatHost((_a = config == null ? void 0 : config.host) != null ? _a : "http://127.0.0.1:11434");
|
|
}
|
|
this.fetch = (_b = config == null ? void 0 : config.fetch) != null ? _b : fetch;
|
|
}
|
|
// Abort any ongoing streamed requests to Ollama
|
|
abort() {
|
|
for (const request of this.ongoingStreamedRequests) {
|
|
request.abort();
|
|
}
|
|
this.ongoingStreamedRequests.length = 0;
|
|
}
|
|
/**
|
|
* Processes a request to the Ollama server. If the request is streamable, it will return a
|
|
* AbortableAsyncIterator that yields the response messages. Otherwise, it will return the response
|
|
* object.
|
|
* @param endpoint {string} - The endpoint to send the request to.
|
|
* @param request {object} - The request object to send to the endpoint.
|
|
* @protected {T | AbortableAsyncIterator<T>} - The response object or a AbortableAsyncIterator that yields
|
|
* response messages.
|
|
* @throws {Error} - If the response body is missing or if the response is an error.
|
|
* @returns {Promise<T | AbortableAsyncIterator<T>>} - The response object or a AbortableAsyncIterator that yields the streamed response.
|
|
*/
|
|
async processStreamableRequest(endpoint, request) {
|
|
var _a;
|
|
request.stream = (_a = request.stream) != null ? _a : false;
|
|
const host = `${this.config.host}/api/${endpoint}`;
|
|
if (request.stream) {
|
|
const abortController = new AbortController();
|
|
const response2 = await post(this.fetch, host, request, {
|
|
signal: abortController.signal,
|
|
headers: this.config.headers
|
|
});
|
|
if (!response2.body) {
|
|
throw new Error("Missing body");
|
|
}
|
|
const itr = parseJSON(response2.body);
|
|
const abortableAsyncIterator = new AbortableAsyncIterator(
|
|
abortController,
|
|
itr,
|
|
() => {
|
|
const i = this.ongoingStreamedRequests.indexOf(abortableAsyncIterator);
|
|
if (i > -1) {
|
|
this.ongoingStreamedRequests.splice(i, 1);
|
|
}
|
|
}
|
|
);
|
|
this.ongoingStreamedRequests.push(abortableAsyncIterator);
|
|
return abortableAsyncIterator;
|
|
}
|
|
const response = await post(this.fetch, host, request, {
|
|
headers: this.config.headers
|
|
});
|
|
return await response.json();
|
|
}
|
|
/**
|
|
* Encodes an image to base64 if it is a Uint8Array.
|
|
* @param image {Uint8Array | string} - The image to encode.
|
|
* @returns {Promise<string>} - The base64 encoded image.
|
|
*/
|
|
async encodeImage(image) {
|
|
if (typeof image !== "string") {
|
|
const uint8Array = new Uint8Array(image);
|
|
let byteString = "";
|
|
const len = uint8Array.byteLength;
|
|
for (let i = 0; i < len; i++) {
|
|
byteString += String.fromCharCode(uint8Array[i]);
|
|
}
|
|
return btoa(byteString);
|
|
}
|
|
return image;
|
|
}
|
|
/**
|
|
* Generates a response from a text prompt.
|
|
* @param request {GenerateRequest} - The request object.
|
|
* @returns {Promise<GenerateResponse | AbortableAsyncIterator<GenerateResponse>>} - The response object or
|
|
* an AbortableAsyncIterator that yields response messages.
|
|
*/
|
|
async generate(request) {
|
|
if (request.images) {
|
|
request.images = await Promise.all(request.images.map(this.encodeImage.bind(this)));
|
|
}
|
|
return this.processStreamableRequest("generate", request);
|
|
}
|
|
/**
|
|
* Chats with the model. The request object can contain messages with images that are either
|
|
* Uint8Arrays or base64 encoded strings. The images will be base64 encoded before sending the
|
|
* request.
|
|
* @param request {ChatRequest} - The request object.
|
|
* @returns {Promise<ChatResponse | AbortableAsyncIterator<ChatResponse>>} - The response object or an
|
|
* AbortableAsyncIterator that yields response messages.
|
|
*/
|
|
async chat(request) {
|
|
if (request.messages) {
|
|
for (const message of request.messages) {
|
|
if (message.images) {
|
|
message.images = await Promise.all(
|
|
message.images.map(this.encodeImage.bind(this))
|
|
);
|
|
}
|
|
}
|
|
}
|
|
return this.processStreamableRequest("chat", request);
|
|
}
|
|
/**
|
|
* Creates a new model from a stream of data.
|
|
* @param request {CreateRequest} - The request object.
|
|
* @returns {Promise<ProgressResponse | AbortableAsyncIterator<ProgressResponse>>} - The response object or a stream of progress responses.
|
|
*/
|
|
async create(request) {
|
|
return this.processStreamableRequest("create", {
|
|
...request
|
|
});
|
|
}
|
|
/**
|
|
* Pulls a model from the Ollama registry. The request object can contain a stream flag to indicate if the
|
|
* response should be streamed.
|
|
* @param request {PullRequest} - The request object.
|
|
* @returns {Promise<ProgressResponse | AbortableAsyncIterator<ProgressResponse>>} - The response object or
|
|
* an AbortableAsyncIterator that yields response messages.
|
|
*/
|
|
async pull(request) {
|
|
return this.processStreamableRequest("pull", {
|
|
name: request.model,
|
|
stream: request.stream,
|
|
insecure: request.insecure
|
|
});
|
|
}
|
|
/**
|
|
* Pushes a model to the Ollama registry. The request object can contain a stream flag to indicate if the
|
|
* response should be streamed.
|
|
* @param request {PushRequest} - The request object.
|
|
* @returns {Promise<ProgressResponse | AbortableAsyncIterator<ProgressResponse>>} - The response object or
|
|
* an AbortableAsyncIterator that yields response messages.
|
|
*/
|
|
async push(request) {
|
|
return this.processStreamableRequest("push", {
|
|
name: request.model,
|
|
stream: request.stream,
|
|
insecure: request.insecure
|
|
});
|
|
}
|
|
/**
|
|
* Deletes a model from the server. The request object should contain the name of the model to
|
|
* delete.
|
|
* @param request {DeleteRequest} - The request object.
|
|
* @returns {Promise<StatusResponse>} - The response object.
|
|
*/
|
|
async delete(request) {
|
|
await del(
|
|
this.fetch,
|
|
`${this.config.host}/api/delete`,
|
|
{ name: request.model },
|
|
{ headers: this.config.headers }
|
|
);
|
|
return { status: "success" };
|
|
}
|
|
/**
|
|
* Copies a model from one name to another. The request object should contain the name of the
|
|
* model to copy and the new name.
|
|
* @param request {CopyRequest} - The request object.
|
|
* @returns {Promise<StatusResponse>} - The response object.
|
|
*/
|
|
async copy(request) {
|
|
await post(this.fetch, `${this.config.host}/api/copy`, { ...request }, {
|
|
headers: this.config.headers
|
|
});
|
|
return { status: "success" };
|
|
}
|
|
/**
|
|
* Lists the models on the server.
|
|
* @returns {Promise<ListResponse>} - The response object.
|
|
* @throws {Error} - If the response body is missing.
|
|
*/
|
|
async list() {
|
|
const response = await get(this.fetch, `${this.config.host}/api/tags`, {
|
|
headers: this.config.headers
|
|
});
|
|
return await response.json();
|
|
}
|
|
/**
|
|
* Shows the metadata of a model. The request object should contain the name of the model.
|
|
* @param request {ShowRequest} - The request object.
|
|
* @returns {Promise<ShowResponse>} - The response object.
|
|
*/
|
|
async show(request) {
|
|
const response = await post(this.fetch, `${this.config.host}/api/show`, {
|
|
...request
|
|
}, {
|
|
headers: this.config.headers
|
|
});
|
|
return await response.json();
|
|
}
|
|
/**
|
|
* Embeds text input into vectors.
|
|
* @param request {EmbedRequest} - The request object.
|
|
* @returns {Promise<EmbedResponse>} - The response object.
|
|
*/
|
|
async embed(request) {
|
|
const response = await post(this.fetch, `${this.config.host}/api/embed`, {
|
|
...request
|
|
}, {
|
|
headers: this.config.headers
|
|
});
|
|
return await response.json();
|
|
}
|
|
/**
|
|
* Embeds a text prompt into a vector.
|
|
* @param request {EmbeddingsRequest} - The request object.
|
|
* @returns {Promise<EmbeddingsResponse>} - The response object.
|
|
*/
|
|
async embeddings(request) {
|
|
const response = await post(this.fetch, `${this.config.host}/api/embeddings`, {
|
|
...request
|
|
}, {
|
|
headers: this.config.headers
|
|
});
|
|
return await response.json();
|
|
}
|
|
/**
|
|
* Lists the running models on the server
|
|
* @returns {Promise<ListResponse>} - The response object.
|
|
* @throws {Error} - If the response body is missing.
|
|
*/
|
|
async ps() {
|
|
const response = await get(this.fetch, `${this.config.host}/api/ps`, {
|
|
headers: this.config.headers
|
|
});
|
|
return await response.json();
|
|
}
|
|
};
|
|
var browser = new Ollama$1();
|
|
|
|
// node_modules/ollama/dist/index.mjs
|
|
var Ollama2 = class extends Ollama$1 {
|
|
async encodeImage(image) {
|
|
if (typeof image !== "string") {
|
|
return Buffer.from(image).toString("base64");
|
|
}
|
|
try {
|
|
if (import_fs.default.existsSync(image)) {
|
|
const fileBuffer = await import_fs.promises.readFile((0, import_path.resolve)(image));
|
|
return Buffer.from(fileBuffer).toString("base64");
|
|
}
|
|
} catch (e) {
|
|
}
|
|
return image;
|
|
}
|
|
/**
|
|
* checks if a file exists
|
|
* @param path {string} - The path to the file
|
|
* @private @internal
|
|
* @returns {Promise<boolean>} - Whether the file exists or not
|
|
*/
|
|
async fileExists(path) {
|
|
try {
|
|
await import_fs.promises.access(path);
|
|
return true;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
async create(request) {
|
|
if (request.from && await this.fileExists((0, import_path.resolve)(request.from))) {
|
|
throw Error("Creating with a local path is not currently supported from ollama-js");
|
|
}
|
|
if (request.stream) {
|
|
return super.create(request);
|
|
} else {
|
|
return super.create(request);
|
|
}
|
|
}
|
|
};
|
|
var index = new Ollama2();
|
|
|
|
// src/cache.ts
|
|
var import_crypto = require("crypto");
|
|
|
|
// package.json
|
|
var version2 = "0.2.0";
|
|
|
|
// node_modules/eventemitter3/index.mjs
|
|
var import_index = __toESM(require_eventemitter3(), 1);
|
|
|
|
// node_modules/p-timeout/index.js
|
|
var TimeoutError = class extends Error {
|
|
constructor(message) {
|
|
super(message);
|
|
this.name = "TimeoutError";
|
|
}
|
|
};
|
|
var AbortError = class extends Error {
|
|
constructor(message) {
|
|
super();
|
|
this.name = "AbortError";
|
|
this.message = message;
|
|
}
|
|
};
|
|
var getDOMException = (errorMessage) => globalThis.DOMException === void 0 ? new AbortError(errorMessage) : new DOMException(errorMessage);
|
|
var getAbortedReason = (signal) => {
|
|
const reason = signal.reason === void 0 ? getDOMException("This operation was aborted.") : signal.reason;
|
|
return reason instanceof Error ? reason : getDOMException(reason);
|
|
};
|
|
function pTimeout(promise, options) {
|
|
const {
|
|
milliseconds,
|
|
fallback,
|
|
message,
|
|
customTimers = { setTimeout, clearTimeout }
|
|
} = options;
|
|
let timer;
|
|
let abortHandler;
|
|
const wrappedPromise = new Promise((resolve2, reject) => {
|
|
if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
|
|
throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
|
|
}
|
|
if (options.signal) {
|
|
const { signal } = options;
|
|
if (signal.aborted) {
|
|
reject(getAbortedReason(signal));
|
|
}
|
|
abortHandler = () => {
|
|
reject(getAbortedReason(signal));
|
|
};
|
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
}
|
|
if (milliseconds === Number.POSITIVE_INFINITY) {
|
|
promise.then(resolve2, reject);
|
|
return;
|
|
}
|
|
const timeoutError = new TimeoutError();
|
|
timer = customTimers.setTimeout.call(void 0, () => {
|
|
if (fallback) {
|
|
try {
|
|
resolve2(fallback());
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
return;
|
|
}
|
|
if (typeof promise.cancel === "function") {
|
|
promise.cancel();
|
|
}
|
|
if (message === false) {
|
|
resolve2();
|
|
} else if (message instanceof Error) {
|
|
reject(message);
|
|
} else {
|
|
timeoutError.message = message != null ? message : `Promise timed out after ${milliseconds} milliseconds`;
|
|
reject(timeoutError);
|
|
}
|
|
}, milliseconds);
|
|
(async () => {
|
|
try {
|
|
resolve2(await promise);
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
})();
|
|
});
|
|
const cancelablePromise = wrappedPromise.finally(() => {
|
|
cancelablePromise.clear();
|
|
if (abortHandler && options.signal) {
|
|
options.signal.removeEventListener("abort", abortHandler);
|
|
}
|
|
});
|
|
cancelablePromise.clear = () => {
|
|
customTimers.clearTimeout.call(void 0, timer);
|
|
timer = void 0;
|
|
};
|
|
return cancelablePromise;
|
|
}
|
|
|
|
// node_modules/p-queue/dist/lower-bound.js
|
|
function lowerBound(array, value, comparator) {
|
|
let first = 0;
|
|
let count = array.length;
|
|
while (count > 0) {
|
|
const step = Math.trunc(count / 2);
|
|
let it = first + step;
|
|
if (comparator(array[it], value) <= 0) {
|
|
first = ++it;
|
|
count -= step + 1;
|
|
} else {
|
|
count = step;
|
|
}
|
|
}
|
|
return first;
|
|
}
|
|
|
|
// node_modules/p-queue/dist/priority-queue.js
|
|
var _queue;
|
|
var PriorityQueue = class {
|
|
constructor() {
|
|
__privateAdd(this, _queue, []);
|
|
}
|
|
enqueue(run, options) {
|
|
options = {
|
|
priority: 0,
|
|
...options
|
|
};
|
|
const element = {
|
|
priority: options.priority,
|
|
run
|
|
};
|
|
if (this.size && __privateGet(this, _queue)[this.size - 1].priority >= options.priority) {
|
|
__privateGet(this, _queue).push(element);
|
|
return;
|
|
}
|
|
const index2 = lowerBound(__privateGet(this, _queue), element, (a, b) => b.priority - a.priority);
|
|
__privateGet(this, _queue).splice(index2, 0, element);
|
|
}
|
|
dequeue() {
|
|
const item = __privateGet(this, _queue).shift();
|
|
return item == null ? void 0 : item.run;
|
|
}
|
|
filter(options) {
|
|
return __privateGet(this, _queue).filter((element) => element.priority === options.priority).map((element) => element.run);
|
|
}
|
|
get size() {
|
|
return __privateGet(this, _queue).length;
|
|
}
|
|
};
|
|
_queue = new WeakMap();
|
|
|
|
// node_modules/p-queue/dist/index.js
|
|
var _carryoverConcurrencyCount, _isIntervalIgnored, _intervalCount, _intervalCap, _interval, _intervalEnd, _intervalId, _timeoutId, _queue2, _queueClass, _pending, _concurrency, _isPaused, _throwOnTimeout, _PQueue_instances, doesIntervalAllowAnother_get, doesConcurrentAllowAnother_get, next_fn, onResumeInterval_fn, isIntervalPaused_get, tryToStartAnother_fn, initializeIntervalIfNeeded_fn, onInterval_fn, processQueue_fn, throwOnAbort_fn, onEvent_fn;
|
|
var PQueue = class extends import_index.default {
|
|
// TODO: The `throwOnTimeout` option should affect the return types of `add()` and `addAll()`
|
|
constructor(options) {
|
|
var _a, _b, _c, _d;
|
|
super();
|
|
__privateAdd(this, _PQueue_instances);
|
|
__privateAdd(this, _carryoverConcurrencyCount);
|
|
__privateAdd(this, _isIntervalIgnored);
|
|
__privateAdd(this, _intervalCount, 0);
|
|
__privateAdd(this, _intervalCap);
|
|
__privateAdd(this, _interval);
|
|
__privateAdd(this, _intervalEnd, 0);
|
|
__privateAdd(this, _intervalId);
|
|
__privateAdd(this, _timeoutId);
|
|
__privateAdd(this, _queue2);
|
|
__privateAdd(this, _queueClass);
|
|
__privateAdd(this, _pending, 0);
|
|
// The `!` is needed because of https://github.com/microsoft/TypeScript/issues/32194
|
|
__privateAdd(this, _concurrency);
|
|
__privateAdd(this, _isPaused);
|
|
__privateAdd(this, _throwOnTimeout);
|
|
/**
|
|
Per-operation timeout in milliseconds. Operations fulfill once `timeout` elapses if they haven't already.
|
|
|
|
Applies to each future operation.
|
|
*/
|
|
__publicField(this, "timeout");
|
|
options = {
|
|
carryoverConcurrencyCount: false,
|
|
intervalCap: Number.POSITIVE_INFINITY,
|
|
interval: 0,
|
|
concurrency: Number.POSITIVE_INFINITY,
|
|
autoStart: true,
|
|
queueClass: PriorityQueue,
|
|
...options
|
|
};
|
|
if (!(typeof options.intervalCap === "number" && options.intervalCap >= 1)) {
|
|
throw new TypeError(`Expected \`intervalCap\` to be a number from 1 and up, got \`${(_b = (_a = options.intervalCap) == null ? void 0 : _a.toString()) != null ? _b : ""}\` (${typeof options.intervalCap})`);
|
|
}
|
|
if (options.interval === void 0 || !(Number.isFinite(options.interval) && options.interval >= 0)) {
|
|
throw new TypeError(`Expected \`interval\` to be a finite number >= 0, got \`${(_d = (_c = options.interval) == null ? void 0 : _c.toString()) != null ? _d : ""}\` (${typeof options.interval})`);
|
|
}
|
|
__privateSet(this, _carryoverConcurrencyCount, options.carryoverConcurrencyCount);
|
|
__privateSet(this, _isIntervalIgnored, options.intervalCap === Number.POSITIVE_INFINITY || options.interval === 0);
|
|
__privateSet(this, _intervalCap, options.intervalCap);
|
|
__privateSet(this, _interval, options.interval);
|
|
__privateSet(this, _queue2, new options.queueClass());
|
|
__privateSet(this, _queueClass, options.queueClass);
|
|
this.concurrency = options.concurrency;
|
|
this.timeout = options.timeout;
|
|
__privateSet(this, _throwOnTimeout, options.throwOnTimeout === true);
|
|
__privateSet(this, _isPaused, options.autoStart === false);
|
|
}
|
|
get concurrency() {
|
|
return __privateGet(this, _concurrency);
|
|
}
|
|
set concurrency(newConcurrency) {
|
|
if (!(typeof newConcurrency === "number" && newConcurrency >= 1)) {
|
|
throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${newConcurrency}\` (${typeof newConcurrency})`);
|
|
}
|
|
__privateSet(this, _concurrency, newConcurrency);
|
|
__privateMethod(this, _PQueue_instances, processQueue_fn).call(this);
|
|
}
|
|
async add(function_, options = {}) {
|
|
options = {
|
|
timeout: this.timeout,
|
|
throwOnTimeout: __privateGet(this, _throwOnTimeout),
|
|
...options
|
|
};
|
|
return new Promise((resolve2, reject) => {
|
|
__privateGet(this, _queue2).enqueue(async () => {
|
|
var _a;
|
|
__privateWrapper(this, _pending)._++;
|
|
__privateWrapper(this, _intervalCount)._++;
|
|
try {
|
|
(_a = options.signal) == null ? void 0 : _a.throwIfAborted();
|
|
let operation = function_({ signal: options.signal });
|
|
if (options.timeout) {
|
|
operation = pTimeout(Promise.resolve(operation), { milliseconds: options.timeout });
|
|
}
|
|
if (options.signal) {
|
|
operation = Promise.race([operation, __privateMethod(this, _PQueue_instances, throwOnAbort_fn).call(this, options.signal)]);
|
|
}
|
|
const result = await operation;
|
|
resolve2(result);
|
|
this.emit("completed", result);
|
|
} catch (error) {
|
|
if (error instanceof TimeoutError && !options.throwOnTimeout) {
|
|
resolve2();
|
|
return;
|
|
}
|
|
reject(error);
|
|
this.emit("error", error);
|
|
} finally {
|
|
__privateMethod(this, _PQueue_instances, next_fn).call(this);
|
|
}
|
|
}, options);
|
|
this.emit("add");
|
|
__privateMethod(this, _PQueue_instances, tryToStartAnother_fn).call(this);
|
|
});
|
|
}
|
|
async addAll(functions, options) {
|
|
return Promise.all(functions.map(async (function_) => this.add(function_, options)));
|
|
}
|
|
/**
|
|
Start (or resume) executing enqueued tasks within concurrency limit. No need to call this if queue is not paused (via `options.autoStart = false` or by `.pause()` method.)
|
|
*/
|
|
start() {
|
|
if (!__privateGet(this, _isPaused)) {
|
|
return this;
|
|
}
|
|
__privateSet(this, _isPaused, false);
|
|
__privateMethod(this, _PQueue_instances, processQueue_fn).call(this);
|
|
return this;
|
|
}
|
|
/**
|
|
Put queue execution on hold.
|
|
*/
|
|
pause() {
|
|
__privateSet(this, _isPaused, true);
|
|
}
|
|
/**
|
|
Clear the queue.
|
|
*/
|
|
clear() {
|
|
__privateSet(this, _queue2, new (__privateGet(this, _queueClass))());
|
|
}
|
|
/**
|
|
Can be called multiple times. Useful if you for example add additional items at a later time.
|
|
|
|
@returns A promise that settles when the queue becomes empty.
|
|
*/
|
|
async onEmpty() {
|
|
if (__privateGet(this, _queue2).size === 0) {
|
|
return;
|
|
}
|
|
await __privateMethod(this, _PQueue_instances, onEvent_fn).call(this, "empty");
|
|
}
|
|
/**
|
|
@returns A promise that settles when the queue size is less than the given limit: `queue.size < limit`.
|
|
|
|
If you want to avoid having the queue grow beyond a certain size you can `await queue.onSizeLessThan()` before adding a new item.
|
|
|
|
Note that this only limits the number of items waiting to start. There could still be up to `concurrency` jobs already running that this call does not include in its calculation.
|
|
*/
|
|
async onSizeLessThan(limit) {
|
|
if (__privateGet(this, _queue2).size < limit) {
|
|
return;
|
|
}
|
|
await __privateMethod(this, _PQueue_instances, onEvent_fn).call(this, "next", () => __privateGet(this, _queue2).size < limit);
|
|
}
|
|
/**
|
|
The difference with `.onEmpty` is that `.onIdle` guarantees that all work from the queue has finished. `.onEmpty` merely signals that the queue is empty, but it could mean that some promises haven't completed yet.
|
|
|
|
@returns A promise that settles when the queue becomes empty, and all promises have completed; `queue.size === 0 && queue.pending === 0`.
|
|
*/
|
|
async onIdle() {
|
|
if (__privateGet(this, _pending) === 0 && __privateGet(this, _queue2).size === 0) {
|
|
return;
|
|
}
|
|
await __privateMethod(this, _PQueue_instances, onEvent_fn).call(this, "idle");
|
|
}
|
|
/**
|
|
Size of the queue, the number of queued items waiting to run.
|
|
*/
|
|
get size() {
|
|
return __privateGet(this, _queue2).size;
|
|
}
|
|
/**
|
|
Size of the queue, filtered by the given options.
|
|
|
|
For example, this can be used to find the number of items remaining in the queue with a specific priority level.
|
|
*/
|
|
sizeBy(options) {
|
|
return __privateGet(this, _queue2).filter(options).length;
|
|
}
|
|
/**
|
|
Number of running items (no longer in the queue).
|
|
*/
|
|
get pending() {
|
|
return __privateGet(this, _pending);
|
|
}
|
|
/**
|
|
Whether the queue is currently paused.
|
|
*/
|
|
get isPaused() {
|
|
return __privateGet(this, _isPaused);
|
|
}
|
|
};
|
|
_carryoverConcurrencyCount = new WeakMap();
|
|
_isIntervalIgnored = new WeakMap();
|
|
_intervalCount = new WeakMap();
|
|
_intervalCap = new WeakMap();
|
|
_interval = new WeakMap();
|
|
_intervalEnd = new WeakMap();
|
|
_intervalId = new WeakMap();
|
|
_timeoutId = new WeakMap();
|
|
_queue2 = new WeakMap();
|
|
_queueClass = new WeakMap();
|
|
_pending = new WeakMap();
|
|
_concurrency = new WeakMap();
|
|
_isPaused = new WeakMap();
|
|
_throwOnTimeout = new WeakMap();
|
|
_PQueue_instances = new WeakSet();
|
|
doesIntervalAllowAnother_get = function() {
|
|
return __privateGet(this, _isIntervalIgnored) || __privateGet(this, _intervalCount) < __privateGet(this, _intervalCap);
|
|
};
|
|
doesConcurrentAllowAnother_get = function() {
|
|
return __privateGet(this, _pending) < __privateGet(this, _concurrency);
|
|
};
|
|
next_fn = function() {
|
|
__privateWrapper(this, _pending)._--;
|
|
__privateMethod(this, _PQueue_instances, tryToStartAnother_fn).call(this);
|
|
this.emit("next");
|
|
};
|
|
onResumeInterval_fn = function() {
|
|
__privateMethod(this, _PQueue_instances, onInterval_fn).call(this);
|
|
__privateMethod(this, _PQueue_instances, initializeIntervalIfNeeded_fn).call(this);
|
|
__privateSet(this, _timeoutId, void 0);
|
|
};
|
|
isIntervalPaused_get = function() {
|
|
const now = Date.now();
|
|
if (__privateGet(this, _intervalId) === void 0) {
|
|
const delay = __privateGet(this, _intervalEnd) - now;
|
|
if (delay < 0) {
|
|
__privateSet(this, _intervalCount, __privateGet(this, _carryoverConcurrencyCount) ? __privateGet(this, _pending) : 0);
|
|
} else {
|
|
if (__privateGet(this, _timeoutId) === void 0) {
|
|
__privateSet(this, _timeoutId, setTimeout(() => {
|
|
__privateMethod(this, _PQueue_instances, onResumeInterval_fn).call(this);
|
|
}, delay));
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
tryToStartAnother_fn = function() {
|
|
if (__privateGet(this, _queue2).size === 0) {
|
|
if (__privateGet(this, _intervalId)) {
|
|
clearInterval(__privateGet(this, _intervalId));
|
|
}
|
|
__privateSet(this, _intervalId, void 0);
|
|
this.emit("empty");
|
|
if (__privateGet(this, _pending) === 0) {
|
|
this.emit("idle");
|
|
}
|
|
return false;
|
|
}
|
|
if (!__privateGet(this, _isPaused)) {
|
|
const canInitializeInterval = !__privateGet(this, _PQueue_instances, isIntervalPaused_get);
|
|
if (__privateGet(this, _PQueue_instances, doesIntervalAllowAnother_get) && __privateGet(this, _PQueue_instances, doesConcurrentAllowAnother_get)) {
|
|
const job = __privateGet(this, _queue2).dequeue();
|
|
if (!job) {
|
|
return false;
|
|
}
|
|
this.emit("active");
|
|
job();
|
|
if (canInitializeInterval) {
|
|
__privateMethod(this, _PQueue_instances, initializeIntervalIfNeeded_fn).call(this);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
initializeIntervalIfNeeded_fn = function() {
|
|
if (__privateGet(this, _isIntervalIgnored) || __privateGet(this, _intervalId) !== void 0) {
|
|
return;
|
|
}
|
|
__privateSet(this, _intervalId, setInterval(() => {
|
|
__privateMethod(this, _PQueue_instances, onInterval_fn).call(this);
|
|
}, __privateGet(this, _interval)));
|
|
__privateSet(this, _intervalEnd, Date.now() + __privateGet(this, _interval));
|
|
};
|
|
onInterval_fn = function() {
|
|
if (__privateGet(this, _intervalCount) === 0 && __privateGet(this, _pending) === 0 && __privateGet(this, _intervalId)) {
|
|
clearInterval(__privateGet(this, _intervalId));
|
|
__privateSet(this, _intervalId, void 0);
|
|
}
|
|
__privateSet(this, _intervalCount, __privateGet(this, _carryoverConcurrencyCount) ? __privateGet(this, _pending) : 0);
|
|
__privateMethod(this, _PQueue_instances, processQueue_fn).call(this);
|
|
};
|
|
/**
|
|
Executes all queued functions until it reaches the limit.
|
|
*/
|
|
processQueue_fn = function() {
|
|
while (__privateMethod(this, _PQueue_instances, tryToStartAnother_fn).call(this)) {
|
|
}
|
|
};
|
|
throwOnAbort_fn = async function(signal) {
|
|
return new Promise((_resolve, reject) => {
|
|
signal.addEventListener("abort", () => {
|
|
reject(signal.reason);
|
|
}, { once: true });
|
|
});
|
|
};
|
|
onEvent_fn = async function(event, filter) {
|
|
return new Promise((resolve2) => {
|
|
const listener = () => {
|
|
if (filter && !filter()) {
|
|
return;
|
|
}
|
|
this.off(event, listener);
|
|
resolve2();
|
|
};
|
|
this.on(event, listener);
|
|
});
|
|
};
|
|
|
|
// src/globals.ts
|
|
var libVersion = version2;
|
|
var possibleModels = [
|
|
{ name: "llava-llama3 (8B) [default]", model: "llava-llama3:latest" },
|
|
{ name: "llama3.2-vision (11B)", model: "llama3.2-vision:11b" },
|
|
{ name: "llama3.2-vision (90B)", model: "llama3.2-vision:90b" },
|
|
{ name: "llava (7B)", model: "llava:latest" },
|
|
{ name: "llava (13B)", model: "llava:13b" },
|
|
{ name: "llava (34B)", model: "llava:34b" }
|
|
];
|
|
var imagesProcessQueue = new PQueue({ concurrency: 1, timeout: 6e5 });
|
|
|
|
// src/cache.ts
|
|
function getCacheBasePath() {
|
|
return `${app.vault.configDir}/plugins/ai-image-analyzer/cache`;
|
|
}
|
|
function getCachePath(file) {
|
|
const hash = (0, import_crypto.createHash)("md5").update(file.path).digest("hex");
|
|
const folder = `${getCacheBasePath()}`;
|
|
const filename = `${hash}.json`;
|
|
return `${folder}/${filename}`;
|
|
}
|
|
async function isInCache(file) {
|
|
const path = getCachePath(file);
|
|
return await app.vault.adapter.exists(path);
|
|
}
|
|
async function writeCache(file, text) {
|
|
const path = getCachePath(file);
|
|
if (!await this.app.vault.adapter.exists(getCacheBasePath())) {
|
|
await this.app.vault.adapter.mkdir(getCacheBasePath());
|
|
}
|
|
const data = {
|
|
path: file.path,
|
|
text,
|
|
libVersion
|
|
};
|
|
await this.app.vault.adapter.write(path, JSON.stringify(data));
|
|
}
|
|
async function readCache(file) {
|
|
const path = getCachePath(file);
|
|
try {
|
|
if (await this.app.vault.adapter.exists(path)) {
|
|
const raw = await this.app.vault.adapter.read(path);
|
|
return JSON.parse(raw);
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
return null;
|
|
}
|
|
async function removeFromCache(file) {
|
|
const path = getCachePath(file);
|
|
if (await isInCache(file)) {
|
|
return await this.app.vault.adapter.remove(path);
|
|
}
|
|
}
|
|
async function clearCache() {
|
|
const path = getCacheBasePath();
|
|
if (await this.app.vault.adapter.exists(path)) {
|
|
return await this.app.vault.adapter.rmdir(path, true);
|
|
}
|
|
}
|
|
|
|
// src/ollamaManager.ts
|
|
var import_obsidian3 = require("obsidian");
|
|
|
|
// src/util.ts
|
|
var import_obsidian2 = require("obsidian");
|
|
|
|
// src/settings.ts
|
|
var import_obsidian = require("obsidian");
|
|
var DEFAULT_SETTINGS = {
|
|
debug: false,
|
|
ollamaURL: "http://127.0.0.1:11434",
|
|
ollamaToken: "",
|
|
ollamaModel: possibleModels[0],
|
|
prompt: "Describe the image. Just use Keywords. For example: cat, dog, tree. This must be Computer readable. The provided pictures are used in an notebook. Please provide at least 5 Keywords. It will be used to search for the image later.",
|
|
autoClearCache: true
|
|
};
|
|
var settings = Object.assign({}, DEFAULT_SETTINGS);
|
|
async function loadSettings(plugin) {
|
|
settings = Object.assign({}, DEFAULT_SETTINGS, await plugin.loadData());
|
|
}
|
|
async function saveSettings(plugin) {
|
|
setOllama(new Ollama2({
|
|
host: settings.ollamaURL,
|
|
headers: {
|
|
"Authorization": `Bearer ${settings.ollamaToken}`
|
|
}
|
|
}));
|
|
await plugin.saveData(settings);
|
|
}
|
|
var AIImageAnalyzerSettingsTab = class extends import_obsidian.PluginSettingTab {
|
|
constructor(app2, plugin) {
|
|
super(app2, plugin);
|
|
this.plugin = plugin;
|
|
}
|
|
display() {
|
|
const { containerEl } = this;
|
|
containerEl.empty();
|
|
new import_obsidian.Setting(containerEl).setName("Model").setDesc("Select the model to use").addDropdown((dropdown) => dropdown.addOptions(possibleModels.reduce((acc, model) => {
|
|
acc[model.name] = model.name;
|
|
return acc;
|
|
}, {})).setValue(possibleModels.find((model) => model.model === settings.ollamaModel.model).name).onChange(async (value) => {
|
|
settings.ollamaModel = possibleModels.find((model) => model.name === value);
|
|
await saveSettings(this.plugin);
|
|
if (settings.autoClearCache) {
|
|
await clearCache();
|
|
}
|
|
}));
|
|
new import_obsidian.Setting(containerEl).setName("Pull Model").setDesc("Pull the selected model").addButton((button) => button.setButtonText("Pull Model").onClick(async () => await pullImage()));
|
|
new import_obsidian.Setting(containerEl).setName("Clear cache").setDesc("Clear the cache, reanalyzing images could take a while").addButton((button) => button.setButtonText("Clear cache").onClick(async () => {
|
|
await clearCache();
|
|
new import_obsidian.Notice("Cache cleared");
|
|
}));
|
|
new import_obsidian.Setting(containerEl).setName("Debug mode").setDesc("Enable debug mode to see logs in the console").addToggle((toggle) => toggle.setValue(settings.debug).onChange(async (value) => {
|
|
settings.debug = value;
|
|
await saveSettings(this.plugin);
|
|
}));
|
|
new import_obsidian.Setting(containerEl).setName("Ollama server").setHeading();
|
|
new import_obsidian.Setting(containerEl).setName("Ollama URL").setDesc("Set the URL for the Ollama server").addText((text) => text.setPlaceholder("Enter the host (http://127.0.0.1:11434)").setValue(settings.ollamaURL).onChange(async (value) => {
|
|
if (value.length === 0) {
|
|
value = DEFAULT_SETTINGS.ollamaURL;
|
|
}
|
|
settings.ollamaURL = value;
|
|
await saveSettings(this.plugin);
|
|
}));
|
|
new import_obsidian.Setting(containerEl).setName("Ollama Token (Optional)").setDesc("Set the token for authentication with the Ollama server").addText((text) => text.setValue(settings.ollamaToken !== "" ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : "").onChange(async (value) => {
|
|
if (value.contains("\u2022")) {
|
|
return;
|
|
}
|
|
settings.ollamaToken = value;
|
|
await saveSettings(this.plugin);
|
|
}));
|
|
new import_obsidian.Setting(containerEl).setName("Advanced").setHeading();
|
|
new import_obsidian.Setting(containerEl).setName("Prompt").setDesc("Set the prompt for the Ollama model").addTextArea((text) => {
|
|
text.inputEl.rows = 5;
|
|
text.inputEl.cols = 50;
|
|
return text.setPlaceholder("Enter the prompt").setValue(settings.prompt).onChange(async (value) => {
|
|
if (value.length === 0) {
|
|
value = DEFAULT_SETTINGS.prompt;
|
|
}
|
|
settings.prompt = value;
|
|
await saveSettings(this.plugin);
|
|
if (settings.autoClearCache) {
|
|
await clearCache();
|
|
}
|
|
});
|
|
});
|
|
new import_obsidian.Setting(containerEl).setName("Auto clear cache").setDesc("Clear the cache after changing the model or the prompt to reanalyze images (if toggled on the cache will be cleared)").addToggle((toggle) => toggle.setValue(settings.autoClearCache).onChange(async (value) => {
|
|
settings.autoClearCache = value;
|
|
if (value) {
|
|
await clearCache();
|
|
new import_obsidian.Notice("Cache cleared");
|
|
}
|
|
await saveSettings(this.plugin);
|
|
}));
|
|
}
|
|
};
|
|
|
|
// src/util.ts
|
|
var import_canvas = __toESM(require_browser());
|
|
function debugLog(message) {
|
|
if (settings.debug) {
|
|
console.log(message);
|
|
}
|
|
}
|
|
function isImageFile(file) {
|
|
const path = file.path;
|
|
return path.endsWith(".png") || path.endsWith(".jpg") || path.endsWith(".jpeg") || path.endsWith(".webp") || path.endsWith(".svg");
|
|
}
|
|
async function readFile(file) {
|
|
if (file.path.endsWith(".svg")) {
|
|
debugLog("Converting SVG to PNG");
|
|
try {
|
|
const svgData = await this.app.vault.adapter.read(file.path);
|
|
const canvas = (0, import_canvas.createCanvas)(1e3, 1e3);
|
|
const context = canvas.getContext("2d");
|
|
const svgImage = await (0, import_canvas.loadImage)(`data:image/svg+xml;base64,${Buffer.from(svgData).toString("base64")}`);
|
|
context.drawImage(svgImage, 0, 0, 1e3, 1e3);
|
|
return canvas.toDataURL("image/png").split(",")[1];
|
|
} catch (error) {
|
|
console.error("Error converting SVG to PNG:", error);
|
|
throw error;
|
|
}
|
|
} else {
|
|
return (0, import_obsidian2.arrayBufferToBase64)(await app.vault.readBinary(file));
|
|
}
|
|
}
|
|
|
|
// src/ollamaManager.ts
|
|
var ollama;
|
|
async function analyzeImage(file) {
|
|
var _a;
|
|
try {
|
|
return (_a = await imagesProcessQueue.add(() => analyzeImageHandling(file))) != null ? _a : "";
|
|
} catch (e) {
|
|
debugLog(e);
|
|
return "";
|
|
}
|
|
}
|
|
async function analyzeImageHandling(file) {
|
|
debugLog(`Analyzing image ${file.name}`);
|
|
if (!isImageFile(file)) {
|
|
return Promise.reject("File is not an image");
|
|
}
|
|
if (await isInCache(file)) {
|
|
debugLog("Cache hit");
|
|
const text = await readCache(file);
|
|
if (text && text.text !== "") {
|
|
debugLog("Reading from cache");
|
|
debugLog(`Image analyzed ${file.name}`);
|
|
return Promise.resolve(text.text);
|
|
} else {
|
|
debugLog("Failed to read cache");
|
|
debugLog("Removing from cache");
|
|
await removeFromCache(file);
|
|
}
|
|
}
|
|
debugLog(file);
|
|
try {
|
|
const data = await readFile(file);
|
|
const response = await ollama.chat({
|
|
model: settings.ollamaModel.model,
|
|
//llava:13b or llava or llava-llama3
|
|
messages: [{ role: "user", content: settings.prompt, images: [data] }]
|
|
});
|
|
debugLog(response);
|
|
await writeCache(file, response.message.content);
|
|
debugLog(`Image analyzed ${file.name}`);
|
|
return Promise.resolve(response.message.content);
|
|
} catch (e) {
|
|
debugLog(e);
|
|
return Promise.reject("Failed to analyze image");
|
|
}
|
|
}
|
|
async function analyzeImageWithNotice(file) {
|
|
try {
|
|
const notice = new import_obsidian3.Notice("Analyzing image", 0);
|
|
const text = await analyzeImage(file);
|
|
notice.hide();
|
|
new import_obsidian3.Notice("Image analyzed");
|
|
return text;
|
|
} catch (e) {
|
|
debugLog(e);
|
|
new import_obsidian3.Notice("Failed to analyze image");
|
|
new import_obsidian3.Notice(e.toString());
|
|
return "";
|
|
}
|
|
}
|
|
async function analyzeToClipboard(file) {
|
|
try {
|
|
const text = await analyzeImageWithNotice(file);
|
|
await activeWindow.navigator.clipboard.writeText(text);
|
|
new import_obsidian3.Notice("Text copied to clipboard");
|
|
} catch (e) {
|
|
debugLog(e);
|
|
}
|
|
}
|
|
async function pullImage() {
|
|
let progressNotice;
|
|
try {
|
|
new import_obsidian3.Notice(`Pulling ${settings.ollamaModel.name} model started, this may take a while...`);
|
|
const response = await ollama.pull({ model: settings.ollamaModel.model, stream: true });
|
|
progressNotice = new import_obsidian3.Notice(`Pulling ${settings.ollamaModel.name} model 0%`, 0);
|
|
for await (const part of response) {
|
|
debugLog(part);
|
|
if (part.total !== null && part.completed !== null) {
|
|
const percentage = part.completed / part.total * 100;
|
|
if (!isNaN(percentage) && percentage !== Infinity && percentage !== -Infinity) {
|
|
const roundedNumber = percentage.toFixed(2);
|
|
const completed = (part.completed / 1e9).toFixed(2);
|
|
const total = (part.total / 1e9).toFixed(2);
|
|
progressNotice.setMessage(`Pulling ${settings.ollamaModel.name} model ${roundedNumber}% (${completed}GB/${total}GB)`);
|
|
}
|
|
}
|
|
}
|
|
progressNotice.hide();
|
|
new import_obsidian3.Notice(`${settings.ollamaModel.name} model pulled successfully`);
|
|
} catch (e) {
|
|
debugLog(e);
|
|
progressNotice == null ? void 0 : progressNotice.hide();
|
|
new import_obsidian3.Notice(`Failed to pull ${settings.ollamaModel.name} model`);
|
|
new import_obsidian3.Notice(e.toString());
|
|
}
|
|
}
|
|
async function checkOllama() {
|
|
try {
|
|
const models = await ollama.list();
|
|
debugLog(models);
|
|
if (!models.models.some((model) => model.name === settings.ollamaModel.model)) {
|
|
new import_obsidian3.Notice(`No ${settings.ollamaModel.name} model found, please make sure you have pulled it (you can pull it over the settings tab or choose another model)`);
|
|
}
|
|
} catch (e) {
|
|
debugLog(e);
|
|
new import_obsidian3.Notice("Failed to connect to Ollama.");
|
|
new import_obsidian3.Notice(e.toString());
|
|
}
|
|
}
|
|
function setOllama(ollamaInstance) {
|
|
ollama = ollamaInstance;
|
|
}
|
|
|
|
// src/main.ts
|
|
var AIImageAnalyzerPlugin = class extends import_obsidian4.Plugin {
|
|
constructor() {
|
|
super(...arguments);
|
|
this.api = {
|
|
analyzeImage,
|
|
canBeAnalyzed: isImageFile,
|
|
isInCache
|
|
};
|
|
}
|
|
async onload() {
|
|
debugLog("loading ai image analyzer plugin");
|
|
await loadSettings(this);
|
|
setOllama(new Ollama2({
|
|
host: settings.ollamaURL,
|
|
headers: {
|
|
"Authorization": `Bearer ${settings.ollamaToken}`
|
|
}
|
|
}));
|
|
await checkOllama();
|
|
this.addCommand({
|
|
id: "analyze-image-to-clipboard",
|
|
name: "Analyze image to clipboard",
|
|
checkCallback: (checking) => {
|
|
const file = getActiveFile();
|
|
if (file != null && isImageFile(file)) {
|
|
if (!checking) {
|
|
analyzeToClipboard(file);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
this.addCommand({
|
|
id: "analyze-image",
|
|
name: "Analyze image",
|
|
checkCallback: (checking) => {
|
|
const file = getActiveFile();
|
|
if (file != null && isImageFile(file)) {
|
|
if (!checking) {
|
|
analyzeImageWithNotice(file);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
this.addCommand({
|
|
id: "clear-cache-of-active-image",
|
|
name: "Clear cache of active image",
|
|
checkCallback: (checking) => {
|
|
const file = getActiveFile();
|
|
if (file != null && isImageFile(file)) {
|
|
if (!checking) {
|
|
removeFromCache(file);
|
|
new import_obsidian4.Notice("Cache cleared");
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
this.registerEvent(
|
|
this.app.workspace.on("file-menu", (menu, file, _source) => {
|
|
if (file instanceof import_obsidian4.TFile && isImageFile(file)) {
|
|
menu.addItem((item) => {
|
|
item.setTitle("AI analyze image");
|
|
const submenu = item.setSubmenu();
|
|
submenu.addItem(
|
|
(item2) => item2.setTitle("Analyze image to clipboard").setIcon("clipboard").onClick(() => {
|
|
analyzeToClipboard(file);
|
|
})
|
|
);
|
|
submenu.addItem(
|
|
(item2) => item2.setTitle("Analyze image").setIcon("search").onClick(async () => {
|
|
await removeFromCache(file);
|
|
await analyzeImageWithNotice(file);
|
|
})
|
|
);
|
|
submenu.addItem(
|
|
(item2) => item2.setTitle("Clear cache").setIcon("trash").onClick(async () => {
|
|
await removeFromCache(file);
|
|
new import_obsidian4.Notice("Cache cleared");
|
|
})
|
|
);
|
|
});
|
|
}
|
|
})
|
|
);
|
|
this.addSettingTab(new AIImageAnalyzerSettingsTab(this.app, this));
|
|
}
|
|
onunload() {
|
|
imagesProcessQueue.clear();
|
|
debugLog("unloading ai image analyzer plugin");
|
|
}
|
|
};
|
|
function getActiveFile() {
|
|
var _a, _b;
|
|
return (_b = (_a = this.app.workspace.activeEditor) == null ? void 0 : _a.file) != null ? _b : this.app.workspace.getActiveFile();
|
|
}
|
|
|
|
/* nosourcemap */ |