import { AvailableModelsRequester } from "../networks/llm_requester.js";
class EndpointStorage {
constructor() {
this.init_database();
this.load_local_endpoints();
this.create_endpoint_and_api_key_items();
this.fill_available_models_select();
}
init_database() {
this.db = new Dexie("endpoints");
this.db.version(1).stores({
endpoints:
"index, endpoint, api_key, need_protect, available_models",
});
this.db.endpoints.count((count) => {
console.log(`${count} endpoints loaded.`);
});
}
clear_database() {
this.db.endpoints.clear();
console.log("endpoints cleared.");
}
async load_local_endpoints() {
fetch("/endpoints")
.then((response) => response.json())
.then((data) => {
if (data.error) {
console.error(data.error);
return;
}
let count = Object.keys(data).length;
console.log(`${count} local endpoints loaded.`);
// data is array of endpint items, each item has 4 keys:
// - `endpoint`, `api_key`, `api_type`, `need_protect`
// add these to db.endpoints
data.forEach((endpoint) => {
this.db.endpoints.put({
index: endpoint.endpoint,
endpoint: endpoint.endpoint,
api_key: endpoint.api_key,
need_protect: endpoint.need_protect || true,
});
});
});
}
generate_endpoint_and_api_key_item_html() {
let endpoint_and_api_key_item_html = `
`;
return endpoint_and_api_key_item_html;
}
add_endpoint_and_api_key_item() {
let endpoint_and_api_key_items = $("#endpoint-and-api-key-items");
let endpoint_and_api_key_item = $(
this.generate_endpoint_and_api_key_item_html()
);
endpoint_and_api_key_items.append(endpoint_and_api_key_item);
this.bind_endpoint_and_api_key_buttons(endpoint_and_api_key_item);
}
create_endpoint_and_api_key_items() {
let endpoint_and_api_key_items = $("#endpoint-and-api-key-items");
let endpoints = this.db.endpoints;
endpoint_and_api_key_items.empty();
endpoints.each((row) => {
if (row.need_protect) {
return;
}
let endpoint_and_api_key_item_html =
this.generate_endpoint_and_api_key_item_html();
let endpoint_and_api_key_item = $(endpoint_and_api_key_item_html);
let endpoint_input =
endpoint_and_api_key_item.find(".endpoint-input");
endpoint_input.val(row.endpoint);
let api_key_input =
endpoint_and_api_key_item.find(".api-key-input");
api_key_input.val(row.api_key);
endpoint_and_api_key_items.append(endpoint_and_api_key_item);
this.bind_endpoint_and_api_key_buttons(endpoint_and_api_key_item);
});
endpoint_and_api_key_items.hide();
}
bind_endpoint_and_api_key_buttons(endpoint_and_api_key_item) {
let self = this;
// console.log("endpoint_and_api_key_item:", endpoint_and_api_key_item);
let endpoint_submit_button = endpoint_and_api_key_item.find(
".submit-endpoint-button"
);
endpoint_submit_button.click(function () {
let endpoint_input =
endpoint_and_api_key_item.find(".endpoint-input");
let endpoint_input_value = endpoint_input.val().trim();
let api_key_input =
endpoint_and_api_key_item.find(".api-key-input");
let api_key_input_value = api_key_input.val().trim();
if (endpoint_input_value.trim() === "") {
console.log("Endpoint is empty.");
return;
} else {
self.db.endpoints.put({
index: endpoint_input_value,
endpoint: endpoint_input_value,
api_key: api_key_input_value,
need_protect: false,
});
console.log(`new_endpoint: ${endpoint_input_value}`);
}
self.fill_available_models_select(endpoint_input_value);
});
let remove_endpoint_buttons = endpoint_and_api_key_item.find(
".remove-endpoint-button"
);
remove_endpoint_buttons.click(function () {
let endpoint_input =
endpoint_and_api_key_item.find(".endpoint-input");
let endpoint_input_value = endpoint_input.val();
endpoint_and_api_key_item.remove();
if (
endpoint_input_value.trim() === "" ||
self.db.endpoints.get(endpoint_input_value) === undefined
) {
console.log("Endpoint not in endpoints");
} else {
self.db.endpoints.delete(endpoint_input_value);
}
console.log(`remove endpoint: ${endpoint_input_value}`);
// TODO: remove models of current endpoint from available_models_select
});
}
fetch_available_models(endpoint) {
console.log("fetch available models for endpoint:", endpoint);
// if endpoint not starts with http(s), skip
// such as "user-customized", which is used for other local functions or agents
if (endpoint.startsWith("http")) {
let available_models_requester = new AvailableModelsRequester(
endpoint
);
// update available_models field of endpoint index in db.endpoints
return available_models_requester.get().then((available_models) => {
this.db.endpoints.update(endpoint, {
available_models: JSON.stringify(available_models),
});
return available_models;
});
} else {
return Promise.resolve([]);
}
}
fill_available_models_select() {
// fetch available_models for all endpoints, and then fill available_models_select
let available_models_select = $("#available-models-select");
available_models_select.empty();
this.db.endpoints.toArray().then((endpoints) => {
let promises = endpoints.map((row) => {
return this.fetch_available_models(row.endpoint).then(
(available_models) => {
available_models.forEach((model_id) => {
// if model duplicated in available_models_select values,
// then attach endpoint host name
let model_name = model_id;
let endpoint_hostname = new URL(
row.endpoint
).hostname
.split(".")[0]
.split("-")[0];
let model_id_with_endpoint = `${row.endpoint}|${model_id}`;
model_name = `${model_id} (${endpoint_hostname})`;
const option = new Option(
model_name,
model_id_with_endpoint
);
available_models_select.append(option);
});
}
);
});
Promise.all(promises).then(() => {
this.set_default_model();
});
});
}
set_default_model() {
let storage_default_model = localStorage.getItem("default_model");
// format of storage_default_model is `{endpoint}|{model_id}`
// if storage_default_model is null, or not in the available_models_select,
// set as the first one of available_models_select
let select = $("#available-models-select");
if (
storage_default_model &&
select.find(`option[value="${storage_default_model}"]`).length > 0
) {
select.val(storage_default_model);
console.log(
"load default model:",
localStorage.getItem("default_model")
);
} else {
let new_storage_default_model = select.find("option:first").val();
select.val(new_storage_default_model);
localStorage.setItem("default_model", new_storage_default_model);
console.log(
"set new default model:",
localStorage.getItem("default_model")
);
}
}
}
export let endpoint_storage = new EndpointStorage();