Files
tubestation/toolkit/mozapps/extensions/internal/ModelHubProvider.sys.mjs
Nick Grato 8efa7e4983 Bug 1944691 - Local model managment - Display local models r=Mardak,extension-reviewers,rpl
basic hooking up model hub on init calculating stuff from files

Differential Revision: https://phabricator.services.mozilla.com/D240550
2025-04-29 23:30:50 +00:00

170 lines
3.9 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
AddonManagerPrivate: "resource://gre/modules/AddonManager.sys.mjs",
computeSha256HashAsString:
"resource://gre/modules/addons/crypto-utils.sys.mjs",
ModelHub: "chrome://global/content/ml/ModelHub.sys.mjs",
});
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"MODELHUB_PROVIDER_ENABLED",
"browser.ml.modelHubProvider",
false,
(_pref, _old, val) => ModelHubProvider[val ? "init" : "shutdown"]()
);
const MODELHUB_ADDON_ID_SUFFIX = "@modelhub.mozilla.org";
const MODELHUB_ADDON_TYPE = "mlmodel";
export class ModelHubAddonWrapper {
#provider;
id;
name;
version;
totalSize;
lastUsed;
updateDate;
constructor(params) {
this.#provider = params.provider;
this.id = params.id;
this.name = params.name;
this.version = params.version;
this.totalSize = params.totalSize;
this.lastUsed = params.lastUsed;
this.updateDate = params.updateDate;
}
async uninstall() {
await this.#provider.modelHub.deleteModels({
model: this.name,
revision: this.version,
});
await this.#provider.onUninstalled(this);
}
get isActive() {
return true;
}
get isCompatible() {
return true;
}
get permissions() {
return lazy.AddonManager.PERM_CAN_UNINSTALL;
}
get type() {
return MODELHUB_ADDON_TYPE;
}
}
export const ModelHubProvider = {
cache: new Map(),
modelHub: null,
get name() {
return "ModelHubProvider";
},
async getAddonsByTypes(types) {
if (!lazy.MODELHUB_PROVIDER_ENABLED) {
return [];
}
const match = types?.includes?.(MODELHUB_ADDON_TYPE);
return match
? await this.refreshAddonCache().then(() =>
Array.from(this.cache.values())
)
: [];
},
async getAddonByID(id) {
return this.cache.get(id);
},
observe(_subject, topic, _data) {
switch (topic) {
case "browser-delayed-startup-finished":
Services.obs.removeObserver(this, "browser-delayed-startup-finished");
this.init();
break;
}
},
shutdown() {
if (this.initialized) {
lazy.AddonManagerPrivate.unregisterProvider(this);
this.initialized = false;
this.clearAddonCache();
}
},
async init() {
if (!lazy.MODELHUB_PROVIDER_ENABLED || this.initialized) {
return;
}
this.initialized = true;
lazy.AddonManagerPrivate.registerProvider(this, [MODELHUB_ADDON_TYPE]);
this.modelHub = new lazy.ModelHub();
await this.refreshAddonCache();
},
async onUninstalled(addon) {
if (!this.cache.has(addon.id)) {
return;
}
this.cache.delete(addon.id);
lazy.AddonManagerPrivate.callAddonListeners("onUninstalled", addon);
},
async clearAddonCache() {
this.cache.clear();
},
getWrapperIdForModel(model) {
return [
lazy.computeSha256HashAsString(`${model.name}:${model.revision}`),
MODELHUB_ADDON_ID_SUFFIX,
].join("");
},
async refreshAddonCache() {
const models = await this.modelHub.listModels();
for (const model of models) {
const { metadata } = await this.modelHub.listFiles({
model: model.name,
revision: model.revision,
});
const id = this.getWrapperIdForModel(model);
const wrapper = new ModelHubAddonWrapper({
provider: this,
id,
name: model.name,
version: model.revision,
totalSize: metadata.totalSize,
lastUsed: new Date(metadata.lastUsed),
updateDate: new Date(metadata.updateDate),
});
this.cache.set(wrapper.id, wrapper);
}
},
};
Services.obs.addObserver(ModelHubProvider, "browser-delayed-startup-finished");