feat: allowing user to fetch models from github
Signed-off-by: James <james@jan.ai>
This commit is contained in:
parent
cb25a69909
commit
d982dce090
@ -95,6 +95,7 @@ const createConversation = (conversation: any) =>
|
|||||||
resolve(undefined);
|
resolve(undefined);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const createMessage = (message: any) =>
|
const createMessage = (message: any) =>
|
||||||
new Promise((resolve) => {
|
new Promise((resolve) => {
|
||||||
if (window && window.electronAPI) {
|
if (window && window.electronAPI) {
|
||||||
|
|||||||
@ -5,54 +5,78 @@ const { app } = require("electron");
|
|||||||
const MODEL_TABLE_CREATION = `
|
const MODEL_TABLE_CREATION = `
|
||||||
CREATE TABLE IF NOT EXISTS models (
|
CREATE TABLE IF NOT EXISTS models (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
slug TEXT NOT NULL,
|
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
description TEXT NOT NULL,
|
short_description TEXT NOT NULL,
|
||||||
avatar_url TEXT,
|
avatar_url TEXT,
|
||||||
long_description TEXT NOT NULL,
|
long_description TEXT NOT NULL,
|
||||||
technical_description TEXT NOT NULL,
|
|
||||||
author TEXT NOT NULL,
|
author TEXT NOT NULL,
|
||||||
version TEXT NOT NULL,
|
version TEXT NOT NULL,
|
||||||
model_url TEXT NOT NULL,
|
model_url TEXT NOT NULL,
|
||||||
nsfw INTEGER NOT NULL,
|
nsfw INTEGER NOT NULL,
|
||||||
greeting TEXT NOT NULL,
|
tags TEXT NOT NULL,
|
||||||
|
default_greeting TEXT NOT NULL,
|
||||||
type TEXT NOT NULL,
|
type TEXT NOT NULL,
|
||||||
file_name TEXT NOT NULL,
|
|
||||||
download_url TEXT NOT NULL,
|
|
||||||
start_download_at INTEGER DEFAULT -1,
|
|
||||||
finish_download_at INTEGER DEFAULT -1,
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
);`;
|
);`;
|
||||||
|
|
||||||
|
const MODEL_VERSION_TABLE_CREATION = `
|
||||||
|
CREATE TABLE IF NOT EXISTS model_versions (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
quant_method TEXT NOT NULL,
|
||||||
|
bits INTEGER NOT NULL,
|
||||||
|
size INTEGER NOT NULL,
|
||||||
|
max_ram_required INTEGER NOT NULL,
|
||||||
|
usecase TEXT NOT NULL,
|
||||||
|
download_link TEXT NOT NULL,
|
||||||
|
model_id TEXT NOT NULL,
|
||||||
|
start_download_at INTEGER DEFAULT -1,
|
||||||
|
finish_download_at INTEGER DEFAULT -1,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);`;
|
||||||
|
|
||||||
const MODEL_TABLE_INSERTION = `
|
const MODEL_TABLE_INSERTION = `
|
||||||
INSERT INTO models (
|
INSERT OR IGNORE INTO models (
|
||||||
id,
|
id,
|
||||||
slug,
|
|
||||||
name,
|
name,
|
||||||
description,
|
short_description,
|
||||||
avatar_url,
|
avatar_url,
|
||||||
long_description,
|
long_description,
|
||||||
technical_description,
|
|
||||||
author,
|
author,
|
||||||
version,
|
version,
|
||||||
model_url,
|
model_url,
|
||||||
nsfw,
|
nsfw,
|
||||||
greeting,
|
tags,
|
||||||
type,
|
default_greeting,
|
||||||
file_name,
|
type
|
||||||
download_url,
|
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)`;
|
||||||
|
|
||||||
|
const MODEL_VERSION_TABLE_INSERTION = `
|
||||||
|
INSERT INTO model_versions (
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
quant_method,
|
||||||
|
bits,
|
||||||
|
size,
|
||||||
|
max_ram_required,
|
||||||
|
usecase,
|
||||||
|
download_link,
|
||||||
|
model_id,
|
||||||
start_download_at
|
start_download_at
|
||||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`;
|
) VALUES (?,?,?,?,?,?,?,?,?,?)`;
|
||||||
|
|
||||||
|
const getDbPath = () => {
|
||||||
|
return path.join(app.getPath("userData"), "jan.db");
|
||||||
|
};
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
const db = new sqlite3.Database(path.join(app.getPath("userData"), "jan.db"));
|
const db = new sqlite3.Database(getDbPath());
|
||||||
console.log(
|
console.debug(`Database located at ${getDbPath()}`);
|
||||||
`Database located at ${path.join(app.getPath("userData"), "jan.db")}`
|
|
||||||
);
|
|
||||||
|
|
||||||
db.serialize(() => {
|
db.serialize(() => {
|
||||||
db.run(MODEL_TABLE_CREATION);
|
db.run(MODEL_TABLE_CREATION);
|
||||||
|
db.run(MODEL_VERSION_TABLE_CREATION);
|
||||||
db.run(
|
db.run(
|
||||||
"CREATE TABLE IF NOT EXISTS conversations ( id INTEGER PRIMARY KEY, name TEXT, model_id TEXT, image TEXT, message TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP);"
|
"CREATE TABLE IF NOT EXISTS conversations ( id INTEGER PRIMARY KEY, name TEXT, model_id TEXT, image TEXT, message TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP);"
|
||||||
);
|
);
|
||||||
@ -67,33 +91,59 @@ function init() {
|
|||||||
/**
|
/**
|
||||||
* Store a model in the database when user start downloading it
|
* Store a model in the database when user start downloading it
|
||||||
*
|
*
|
||||||
* @param model Product
|
* @param params: { model, modelVersion }
|
||||||
*/
|
*/
|
||||||
function storeModel(model: any) {
|
function storeModel(params: any) {
|
||||||
return new Promise((res) => {
|
return new Promise((res) => {
|
||||||
const db = new sqlite3.Database(
|
const db = new sqlite3.Database(getDbPath());
|
||||||
path.join(app.getPath("userData"), "jan.db")
|
console.debug("Inserting", JSON.stringify(params));
|
||||||
);
|
|
||||||
console.debug("Inserting", JSON.stringify(model));
|
const model = params.model;
|
||||||
|
const modelTags = model.tags.join(",");
|
||||||
|
const modelVersion = params.modelVersion;
|
||||||
|
|
||||||
db.serialize(() => {
|
db.serialize(() => {
|
||||||
const stmt = db.prepare(MODEL_TABLE_INSERTION);
|
const stmt = db.prepare(MODEL_TABLE_INSERTION);
|
||||||
stmt.run(
|
stmt.run(
|
||||||
model.id,
|
model.id,
|
||||||
model.slug,
|
|
||||||
model.name,
|
model.name,
|
||||||
model.description,
|
model.shortDescription,
|
||||||
model.avatarUrl,
|
model.avatarUrl,
|
||||||
model.longDescription,
|
model.longDescription,
|
||||||
model.technicalDescription,
|
|
||||||
model.author,
|
model.author,
|
||||||
model.version,
|
model.version,
|
||||||
model.modelUrl,
|
model.modelUrl,
|
||||||
model.nsfw,
|
model.nsfw,
|
||||||
|
modelTags,
|
||||||
model.greeting,
|
model.greeting,
|
||||||
model.type,
|
model.type,
|
||||||
model.fileName,
|
function (err: any) {
|
||||||
model.downloadUrl,
|
if (err) {
|
||||||
Date.now(),
|
// Handle the insertion error here
|
||||||
|
console.error(err.message);
|
||||||
|
res(undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// @ts-ignoreF
|
||||||
|
const id = this.lastID;
|
||||||
|
res(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// insert modelVersion to MODEL_VERSION_TABLE_INSERTION
|
||||||
|
const stmt2 = db.prepare(MODEL_VERSION_TABLE_INSERTION);
|
||||||
|
stmt2.run(
|
||||||
|
modelVersion.id,
|
||||||
|
modelVersion.name,
|
||||||
|
modelVersion.quantMethod,
|
||||||
|
modelVersion.bits,
|
||||||
|
modelVersion.size,
|
||||||
|
modelVersion.maxRamRequired,
|
||||||
|
modelVersion.usecase,
|
||||||
|
modelVersion.downloadLink,
|
||||||
|
model.id,
|
||||||
|
modelVersion.startDownloadAt,
|
||||||
function (err: any) {
|
function (err: any) {
|
||||||
if (err) {
|
if (err) {
|
||||||
// Handle the insertion error here
|
// Handle the insertion error here
|
||||||
@ -108,6 +158,7 @@ function storeModel(model: any) {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
stmt.finalize();
|
stmt.finalize();
|
||||||
|
stmt2.finalize();
|
||||||
});
|
});
|
||||||
|
|
||||||
db.close();
|
db.close();
|
||||||
@ -119,14 +170,15 @@ function storeModel(model: any) {
|
|||||||
*
|
*
|
||||||
* @param model Product
|
* @param model Product
|
||||||
*/
|
*/
|
||||||
function updateFinishedDownloadAt(fileName: string, time: number) {
|
function updateFinishedDownloadAt(modelVersionId: string) {
|
||||||
return new Promise((res) => {
|
return new Promise((res) => {
|
||||||
const db = new sqlite3.Database(
|
const db = new sqlite3.Database(getDbPath());
|
||||||
path.join(app.getPath("userData"), "jan.db")
|
const time = Date.now();
|
||||||
|
console.debug(
|
||||||
|
`Updating finished downloaded model version ${modelVersionId}`
|
||||||
);
|
);
|
||||||
console.debug(`Updating fileName ${fileName} to ${time}`);
|
const stmt = `UPDATE model_versions SET finish_download_at = ? WHERE id = ?`;
|
||||||
const stmt = `UPDATE models SET finish_download_at = ? WHERE file_name = ?`;
|
db.run(stmt, [time, modelVersionId], (err: any) => {
|
||||||
db.run(stmt, [time, fileName], (err: any) => {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
res(undefined);
|
res(undefined);
|
||||||
@ -145,11 +197,9 @@ function updateFinishedDownloadAt(fileName: string, time: number) {
|
|||||||
*/
|
*/
|
||||||
function getUnfinishedDownloadModels() {
|
function getUnfinishedDownloadModels() {
|
||||||
return new Promise((res) => {
|
return new Promise((res) => {
|
||||||
const db = new sqlite3.Database(
|
const db = new sqlite3.Database(getDbPath());
|
||||||
path.join(app.getPath("userData"), "jan.db")
|
|
||||||
);
|
|
||||||
|
|
||||||
const query = `SELECT * FROM models WHERE finish_download_at = -1 ORDER BY start_download_at DESC`;
|
const query = `SELECT * FROM model_versions WHERE finish_download_at = -1 ORDER BY start_download_at DESC`;
|
||||||
db.all(query, (err: Error, row: any) => {
|
db.all(query, (err: Error, row: any) => {
|
||||||
if (row) {
|
if (row) {
|
||||||
res(row);
|
res(row);
|
||||||
@ -161,28 +211,85 @@ function getUnfinishedDownloadModels() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFinishedDownloadModels() {
|
async function getFinishedDownloadModels() {
|
||||||
return new Promise((res) => {
|
const db = new sqlite3.Database(getDbPath());
|
||||||
const db = new sqlite3.Database(
|
|
||||||
path.join(app.getPath("userData"), "jan.db")
|
|
||||||
);
|
|
||||||
|
|
||||||
const query = `SELECT * FROM models WHERE finish_download_at != -1 ORDER BY finish_download_at DESC`;
|
const query = `SELECT * FROM model_versions WHERE finish_download_at != -1 ORDER BY finish_download_at DESC`;
|
||||||
db.all(query, (err: Error, row: any) => {
|
const modelVersions: any = await new Promise((resolve, reject) => {
|
||||||
res(row?.map((item: any) => parseToProduct(item)) ?? []);
|
db.all(query, (err: Error, rows: any[]) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(rows);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
db.close();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const models = await Promise.all(
|
||||||
|
modelVersions.map(async (modelVersion) => {
|
||||||
|
const modelQuery = `SELECT * FROM models WHERE id = ?`;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
db.get(modelQuery, [modelVersion.model_id], (err: Error, row: any) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(row);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const downloadedModels = [];
|
||||||
|
modelVersions.forEach((modelVersion: any) => {
|
||||||
|
const model = models.find((m: any) => m.id === modelVersion.model_id);
|
||||||
|
|
||||||
|
if (!model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const assistantModel = {
|
||||||
|
id: modelVersion.id,
|
||||||
|
name: modelVersion.name,
|
||||||
|
quantMethod: modelVersion.quant_method,
|
||||||
|
bits: modelVersion.bits,
|
||||||
|
size: modelVersion.size,
|
||||||
|
maxRamRequired: modelVersion.max_ram_required,
|
||||||
|
usecase: modelVersion.usecase,
|
||||||
|
downloadLink: modelVersion.download_link,
|
||||||
|
startDownloadAt: modelVersion.start_download_at,
|
||||||
|
finishDownloadAt: modelVersion.finish_download_at,
|
||||||
|
productId: model.id,
|
||||||
|
productName: model.name,
|
||||||
|
shortDescription: model.short_description,
|
||||||
|
longDescription: model.long_description,
|
||||||
|
avatarUrl: model.avatar_url,
|
||||||
|
author: model.author,
|
||||||
|
version: model.version,
|
||||||
|
modelUrl: model.model_url,
|
||||||
|
nsfw: model.nsfw === 0 ? false : true,
|
||||||
|
greeting: model.default_greeting,
|
||||||
|
type: model.type,
|
||||||
|
createdAt: new Date(model.created_at).getTime(),
|
||||||
|
updatedAt: new Date(model.updated_at ?? "").getTime(),
|
||||||
|
status: "",
|
||||||
|
releaseDate: -1,
|
||||||
|
tags: model.tags.split(","),
|
||||||
|
};
|
||||||
|
downloadedModels.push(assistantModel);
|
||||||
|
});
|
||||||
|
|
||||||
|
db.close();
|
||||||
|
|
||||||
|
return downloadedModels;
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteDownloadModel(modelId: string) {
|
function deleteDownloadModel(modelId: string) {
|
||||||
return new Promise((res) => {
|
return new Promise((res) => {
|
||||||
const db = new sqlite3.Database(
|
const db = new sqlite3.Database(getDbPath());
|
||||||
path.join(app.getPath("userData"), "jan.db")
|
console.debug(`Deleting ${modelId}`);
|
||||||
);
|
|
||||||
console.log(`Deleting ${modelId}`);
|
|
||||||
db.serialize(() => {
|
db.serialize(() => {
|
||||||
const stmt = db.prepare("DELETE FROM models WHERE id = ?");
|
const stmt = db.prepare("DELETE FROM model_versions WHERE id = ?");
|
||||||
stmt.run(modelId);
|
stmt.run(modelId);
|
||||||
stmt.finalize();
|
stmt.finalize();
|
||||||
res(modelId);
|
res(modelId);
|
||||||
@ -192,54 +299,101 @@ function deleteDownloadModel(modelId: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getModelById(modelId: string) {
|
async function fetchModelVersion(db: any, versionId: string) {
|
||||||
return new Promise((res) => {
|
return new Promise((resolve, reject) => {
|
||||||
const db = new sqlite3.Database(
|
|
||||||
path.join(app.getPath("userData"), "jan.db")
|
|
||||||
);
|
|
||||||
|
|
||||||
db.get(
|
db.get(
|
||||||
`SELECT * FROM models WHERE id = ?`,
|
"SELECT * FROM model_versions WHERE id = ?",
|
||||||
[modelId],
|
[versionId],
|
||||||
(err: any, row: any) => {
|
(err, row) => {
|
||||||
if (row) {
|
if (err) {
|
||||||
const product = {
|
reject(err);
|
||||||
id: row.id,
|
|
||||||
slug: row.slug,
|
|
||||||
name: row.name,
|
|
||||||
description: row.description,
|
|
||||||
avatarUrl: row.avatar_url,
|
|
||||||
longDescription: row.long_description,
|
|
||||||
technicalDescription: row.technical_description,
|
|
||||||
author: row.author,
|
|
||||||
version: row.version,
|
|
||||||
modelUrl: row.model_url,
|
|
||||||
nsfw: row.nsfw,
|
|
||||||
greeting: row.greeting,
|
|
||||||
type: row.type,
|
|
||||||
inputs: row.inputs,
|
|
||||||
outputs: row.outputs,
|
|
||||||
createdAt: new Date(row.created_at),
|
|
||||||
updatedAt: new Date(row.updated_at),
|
|
||||||
fileName: row.file_name,
|
|
||||||
downloadUrl: row.download_url,
|
|
||||||
};
|
|
||||||
res(product);
|
|
||||||
} else {
|
} else {
|
||||||
res(undefined);
|
if (row) {
|
||||||
|
const product = {
|
||||||
|
id: row.id,
|
||||||
|
slug: row.slug,
|
||||||
|
name: row.name,
|
||||||
|
description: row.description,
|
||||||
|
avatarUrl: row.avatar_url,
|
||||||
|
longDescription: row.long_description,
|
||||||
|
technicalDescription: row.technical_description,
|
||||||
|
author: row.author,
|
||||||
|
version: row.version,
|
||||||
|
modelUrl: row.model_url,
|
||||||
|
nsfw: row.nsfw,
|
||||||
|
greeting: row.greeting,
|
||||||
|
type: row.type,
|
||||||
|
inputs: row.inputs,
|
||||||
|
outputs: row.outputs,
|
||||||
|
createdAt: new Date(row.created_at),
|
||||||
|
updatedAt: new Date(row.updated_at),
|
||||||
|
fileName: row.file_name,
|
||||||
|
downloadUrl: row.download_url,
|
||||||
|
};
|
||||||
|
resolve(product);
|
||||||
|
} else {
|
||||||
|
resolve(undefined);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
db.close();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchModel(db: any, modelId: string) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
db.get("SELECT * FROM models WHERE id = ?", [modelId], (err, row) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(row);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const getModelById = async (versionId: string): Promise<any | undefined> => {
|
||||||
|
const db = new sqlite3.Database(getDbPath());
|
||||||
|
const modelVersion: any | undefined = await fetchModelVersion(db, versionId);
|
||||||
|
if (!modelVersion) return undefined;
|
||||||
|
const model: any | undefined = await fetchModel(db, modelVersion.model_id);
|
||||||
|
if (!model) return undefined;
|
||||||
|
|
||||||
|
const assistantModel = {
|
||||||
|
id: modelVersion.id,
|
||||||
|
name: modelVersion.name,
|
||||||
|
quantMethod: modelVersion.quant_method,
|
||||||
|
bits: modelVersion.bits,
|
||||||
|
size: modelVersion.size,
|
||||||
|
maxRamRequired: modelVersion.max_ram_required,
|
||||||
|
usecase: modelVersion.usecase,
|
||||||
|
downloadLink: modelVersion.download_link,
|
||||||
|
startDownloadAt: modelVersion.start_download_at,
|
||||||
|
finishDownloadAt: modelVersion.finish_download_at,
|
||||||
|
productId: model.id,
|
||||||
|
productName: model.name,
|
||||||
|
shortDescription: model.short_description,
|
||||||
|
longDescription: model.long_description,
|
||||||
|
avatarUrl: model.avatar_url,
|
||||||
|
author: model.author,
|
||||||
|
version: model.version,
|
||||||
|
modelUrl: model.model_url,
|
||||||
|
nsfw: model.nsfw === 0 ? false : true,
|
||||||
|
greeting: model.default_greeting,
|
||||||
|
type: model.type,
|
||||||
|
createdAt: new Date(model.created_at).getTime(),
|
||||||
|
updatedAt: new Date(model.updated_at ?? "").getTime(),
|
||||||
|
status: "",
|
||||||
|
releaseDate: -1,
|
||||||
|
tags: model.tags.split(","),
|
||||||
|
};
|
||||||
|
|
||||||
|
return assistantModel;
|
||||||
|
};
|
||||||
|
|
||||||
function getConversations() {
|
function getConversations() {
|
||||||
return new Promise((res) => {
|
return new Promise((res) => {
|
||||||
const db = new sqlite3.Database(
|
const db = new sqlite3.Database(getDbPath());
|
||||||
path.join(app.getPath("userData"), "jan.db")
|
|
||||||
);
|
|
||||||
|
|
||||||
db.all(
|
db.all(
|
||||||
"SELECT * FROM conversations ORDER BY updated_at DESC",
|
"SELECT * FROM conversations ORDER BY updated_at DESC",
|
||||||
@ -250,11 +404,10 @@ function getConversations() {
|
|||||||
db.close();
|
db.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function storeConversation(conversation: any): Promise<number | undefined> {
|
function storeConversation(conversation: any): Promise<number | undefined> {
|
||||||
return new Promise((res) => {
|
return new Promise((res) => {
|
||||||
const db = new sqlite3.Database(
|
const db = new sqlite3.Database(getDbPath());
|
||||||
path.join(app.getPath("userData"), "jan.db")
|
|
||||||
);
|
|
||||||
|
|
||||||
db.serialize(() => {
|
db.serialize(() => {
|
||||||
const stmt = db.prepare(
|
const stmt = db.prepare(
|
||||||
@ -287,9 +440,7 @@ function storeConversation(conversation: any): Promise<number | undefined> {
|
|||||||
|
|
||||||
function storeMessage(message: any): Promise<number | undefined> {
|
function storeMessage(message: any): Promise<number | undefined> {
|
||||||
return new Promise((res) => {
|
return new Promise((res) => {
|
||||||
const db = new sqlite3.Database(
|
const db = new sqlite3.Database(getDbPath());
|
||||||
path.join(app.getPath("userData"), "jan.db")
|
|
||||||
);
|
|
||||||
|
|
||||||
db.serialize(() => {
|
db.serialize(() => {
|
||||||
const stmt = db.prepare(
|
const stmt = db.prepare(
|
||||||
@ -319,11 +470,10 @@ function storeMessage(message: any): Promise<number | undefined> {
|
|||||||
db.close();
|
db.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateMessage(message: any): Promise<number | undefined> {
|
function updateMessage(message: any): Promise<number | undefined> {
|
||||||
return new Promise((res) => {
|
return new Promise((res) => {
|
||||||
const db = new sqlite3.Database(
|
const db = new sqlite3.Database(getDbPath());
|
||||||
path.join(app.getPath("userData"), "jan.db")
|
|
||||||
);
|
|
||||||
|
|
||||||
db.serialize(() => {
|
db.serialize(() => {
|
||||||
const stmt = db.prepare(
|
const stmt = db.prepare(
|
||||||
@ -340,9 +490,7 @@ function updateMessage(message: any): Promise<number | undefined> {
|
|||||||
|
|
||||||
function deleteConversation(id: any) {
|
function deleteConversation(id: any) {
|
||||||
return new Promise((res) => {
|
return new Promise((res) => {
|
||||||
const db = new sqlite3.Database(
|
const db = new sqlite3.Database(getDbPath());
|
||||||
path.join(app.getPath("userData"), "jan.db")
|
|
||||||
);
|
|
||||||
|
|
||||||
db.serialize(() => {
|
db.serialize(() => {
|
||||||
const deleteConv = db.prepare("DELETE FROM conversations WHERE id = ?");
|
const deleteConv = db.prepare("DELETE FROM conversations WHERE id = ?");
|
||||||
@ -362,9 +510,7 @@ function deleteConversation(id: any) {
|
|||||||
|
|
||||||
function getConversationMessages(conversation_id: any) {
|
function getConversationMessages(conversation_id: any) {
|
||||||
return new Promise((res) => {
|
return new Promise((res) => {
|
||||||
const db = new sqlite3.Database(
|
const db = new sqlite3.Database(getDbPath());
|
||||||
path.join(app.getPath("userData"), "jan.db")
|
|
||||||
);
|
|
||||||
|
|
||||||
const query = `SELECT * FROM messages WHERE conversation_id = ${conversation_id} ORDER BY id DESC`;
|
const query = `SELECT * FROM messages WHERE conversation_id = ${conversation_id} ORDER BY id DESC`;
|
||||||
db.all(query, (err: Error, row: any) => {
|
db.all(query, (err: Error, row: any) => {
|
||||||
@ -374,31 +520,6 @@ function getConversationMessages(conversation_id: any) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseToProduct(row: any) {
|
|
||||||
const product = {
|
|
||||||
id: row.id,
|
|
||||||
slug: row.slug,
|
|
||||||
name: row.name,
|
|
||||||
description: row.description,
|
|
||||||
avatarUrl: row.avatar_url,
|
|
||||||
longDescription: row.long_description,
|
|
||||||
technicalDescription: row.technical_description,
|
|
||||||
author: row.author,
|
|
||||||
version: row.version,
|
|
||||||
modelUrl: row.model_url,
|
|
||||||
nsfw: row.nsfw,
|
|
||||||
greeting: row.greeting,
|
|
||||||
type: row.type,
|
|
||||||
inputs: row.inputs,
|
|
||||||
outputs: row.outputs,
|
|
||||||
createdAt: new Date(row.created_at),
|
|
||||||
updatedAt: new Date(row.updated_at),
|
|
||||||
fileName: row.file_name,
|
|
||||||
downloadUrl: row.download_url,
|
|
||||||
};
|
|
||||||
return product;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
init,
|
init,
|
||||||
getConversations,
|
getConversations,
|
||||||
|
|||||||
@ -8,10 +8,10 @@ const { killPortProcess } = require("kill-port-process");
|
|||||||
let subprocess = null;
|
let subprocess = null;
|
||||||
const PORT = 3928;
|
const PORT = 3928;
|
||||||
|
|
||||||
const initModel = (product) => {
|
const initModel = (fileName) => {
|
||||||
return (
|
return (
|
||||||
new Promise<void>(async (resolve, reject) => {
|
new Promise<void>(async (resolve, reject) => {
|
||||||
if (!product?.fileName) {
|
if (!fileName) {
|
||||||
reject("Model not found, please download again.");
|
reject("Model not found, please download again.");
|
||||||
}
|
}
|
||||||
if (subprocess) {
|
if (subprocess) {
|
||||||
@ -20,7 +20,7 @@ const initModel = (product) => {
|
|||||||
);
|
);
|
||||||
killSubprocess();
|
killSubprocess();
|
||||||
}
|
}
|
||||||
resolve(product?.fileName);
|
resolve(fileName);
|
||||||
})
|
})
|
||||||
// Kill port process if it is already in use
|
// Kill port process if it is already in use
|
||||||
.then((fileName) =>
|
.then((fileName) =>
|
||||||
@ -46,7 +46,7 @@ const initModel = (product) => {
|
|||||||
config.custom_config = {};
|
config.custom_config = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const modelPath = path.join(app.getPath("userData"), product.fileName);
|
const modelPath = path.join(app.getPath("userData"), fileName);
|
||||||
|
|
||||||
config.custom_config.llama_model_path = modelPath;
|
config.custom_config.llama_model_path = modelPath;
|
||||||
|
|
||||||
|
|||||||
@ -47,6 +47,15 @@ const searchModels = async (params) =>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getConfiguredModels = async () =>
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
if (window.electronAPI) {
|
||||||
|
window.electronAPI
|
||||||
|
.invokePluginFunc(MODULE_PATH, "getConfiguredModels")
|
||||||
|
.then((res) => resolve(res));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Register all the above functions and objects with the relevant extension points
|
// Register all the above functions and objects with the relevant extension points
|
||||||
export function init({ register }) {
|
export function init({ register }) {
|
||||||
register("getDownloadedModels", "getDownloadedModels", getDownloadedModels);
|
register("getDownloadedModels", "getDownloadedModels", getDownloadedModels);
|
||||||
@ -54,4 +63,5 @@ export function init({ register }) {
|
|||||||
register("downloadModel", "downloadModel", downloadModel);
|
register("downloadModel", "downloadModel", downloadModel);
|
||||||
register("deleteModel", "deleteModel", deleteModel);
|
register("deleteModel", "deleteModel", deleteModel);
|
||||||
register("searchModels", "searchModels", searchModels);
|
register("searchModels", "searchModels", searchModels);
|
||||||
|
register("getConfiguredModels", "getConfiguredModels", getConfiguredModels);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,95 +1,16 @@
|
|||||||
const path = require("path");
|
|
||||||
const { readdirSync, lstatSync } = require("fs");
|
|
||||||
const { app } = require("electron");
|
|
||||||
const { listModels, listFiles, fileDownloadInfo } = require("@huggingface/hub");
|
const { listModels, listFiles, fileDownloadInfo } = require("@huggingface/hub");
|
||||||
|
const https = require("https");
|
||||||
|
|
||||||
let modelsIterator = undefined;
|
let modelsIterator = undefined;
|
||||||
let currentSearchOwner = undefined;
|
let currentSearchOwner = undefined;
|
||||||
|
|
||||||
const ALL_MODELS = [
|
// Github API
|
||||||
{
|
const githubHostName = "api.github.com";
|
||||||
id: "llama-2-7b-chat.Q4_K_M.gguf.bin",
|
const githubHeaders = {
|
||||||
slug: "llama-2-7b-chat.Q4_K_M.gguf.bin",
|
"User-Agent": "node.js",
|
||||||
name: "Llama 2 7B Chat - GGUF",
|
Accept: "application/vnd.github.v3+json",
|
||||||
description: "medium, balanced quality - recommended",
|
};
|
||||||
avatarUrl:
|
const githubPath = "/repos/janhq/models/contents";
|
||||||
"https://aeiljuispo.cloudimg.io/v7/https://cdn-uploads.huggingface.co/production/uploads/6426d3f3a7723d62b53c259b/tvPikpAzKTKGN5wrpadOJ.jpeg?w=200&h=200&f=face",
|
|
||||||
longDescription:
|
|
||||||
"GGUF is a new format introduced by the llama.cpp team on August 21st 2023. It is a replacement for GGML, which is no longer supported by llama.cpp. GGUF offers numerous advantages over GGML, such as better tokenisation, and support for special tokens. It is also supports metadata, and is designed to be extensible.",
|
|
||||||
technicalDescription:
|
|
||||||
'GGML_TYPE_Q4_K - "type-1" 4-bit quantization in super-blocks containing 8 blocks, each block having 32 weights. Scales and mins are quantized with 6 bits. This ends up using 4.5 bpw.',
|
|
||||||
author: "The Bloke",
|
|
||||||
version: "1.0.0",
|
|
||||||
modelUrl: "https://google.com",
|
|
||||||
nsfw: false,
|
|
||||||
greeting: "Hello there",
|
|
||||||
type: "LLM",
|
|
||||||
inputs: undefined,
|
|
||||||
outputs: undefined,
|
|
||||||
createdAt: 0,
|
|
||||||
updatedAt: undefined,
|
|
||||||
fileName: "llama-2-7b-chat.Q4_K_M.gguf.bin",
|
|
||||||
downloadUrl:
|
|
||||||
"https://huggingface.co/TheBloke/Llama-2-7b-Chat-GGUF/resolve/main/llama-2-7b-chat.Q4_K_M.gguf",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "llama-2-13b-chat.Q4_K_M.gguf",
|
|
||||||
slug: "llama-2-13b-chat.Q4_K_M.gguf",
|
|
||||||
name: "Llama 2 13B Chat - GGUF",
|
|
||||||
description:
|
|
||||||
"medium, balanced quality - not recommended for RAM 16GB and below",
|
|
||||||
avatarUrl:
|
|
||||||
"https://aeiljuispo.cloudimg.io/v7/https://cdn-uploads.huggingface.co/production/uploads/6426d3f3a7723d62b53c259b/tvPikpAzKTKGN5wrpadOJ.jpeg?w=200&h=200&f=face",
|
|
||||||
longDescription:
|
|
||||||
"GGUF is a new format introduced by the llama.cpp team on August 21st 2023. It is a replacement for GGML, which is no longer supported by llama.cpp. GGUF offers numerous advantages over GGML, such as better tokenisation, and support for special tokens. It is also supports metadata, and is designed to be extensible.",
|
|
||||||
technicalDescription:
|
|
||||||
'GGML_TYPE_Q4_K - "type-1" 4-bit quantization in super-blocks containing 8 blocks, each block having 32 weights. Scales and mins are quantized with 6 bits. This ends up using 4.5 bpw.',
|
|
||||||
author: "The Bloke",
|
|
||||||
version: "1.0.0",
|
|
||||||
modelUrl: "https://google.com",
|
|
||||||
nsfw: false,
|
|
||||||
greeting: "Hello there",
|
|
||||||
type: "LLM",
|
|
||||||
inputs: undefined,
|
|
||||||
outputs: undefined,
|
|
||||||
createdAt: 0,
|
|
||||||
updatedAt: undefined,
|
|
||||||
fileName: "llama-2-13b-chat.Q4_K_M.gguf.bin",
|
|
||||||
downloadUrl:
|
|
||||||
"https://huggingface.co/TheBloke/Llama-2-13B-chat-GGUF/resolve/main/llama-2-13b-chat.Q4_K_M.gguf",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
function getDownloadedModels() {
|
|
||||||
const userDataPath = app.getPath("userData");
|
|
||||||
|
|
||||||
const allBinariesName = [];
|
|
||||||
var files = readdirSync(userDataPath);
|
|
||||||
for (var i = 0; i < files.length; i++) {
|
|
||||||
var filename = path.join(userDataPath, files[i]);
|
|
||||||
var stat = lstatSync(filename);
|
|
||||||
if (stat.isDirectory()) {
|
|
||||||
// ignore
|
|
||||||
} else if (filename.endsWith(".bin")) {
|
|
||||||
var binaryName = path.basename(filename);
|
|
||||||
allBinariesName.push(binaryName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const downloadedModels = ALL_MODELS.map((model) => {
|
|
||||||
if (
|
|
||||||
model.fileName &&
|
|
||||||
allBinariesName
|
|
||||||
.map((t) => t.toLowerCase())
|
|
||||||
.includes(model.fileName.toLowerCase())
|
|
||||||
) {
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}).filter((m) => m !== undefined);
|
|
||||||
|
|
||||||
return downloadedModels;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getNextModels = async (count) => {
|
const getNextModels = async (count) => {
|
||||||
const models = [];
|
const models = [];
|
||||||
@ -161,17 +82,131 @@ const listFilesByName = async (modelName) => {
|
|||||||
return fileDownloadInfoMap;
|
return fileDownloadInfoMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
function getAvailableModels() {
|
async function getConfiguredModels() {
|
||||||
const downloadedModelIds = getDownloadedModels().map((model) => model.id);
|
const files = await getModelFiles();
|
||||||
return ALL_MODELS.filter((model) => {
|
|
||||||
if (!downloadedModelIds.includes(model.id)) {
|
const promises = files.map((file) => getContent(file));
|
||||||
return model;
|
const response = await Promise.all(promises);
|
||||||
}
|
|
||||||
|
const models = [];
|
||||||
|
response.forEach((model) => {
|
||||||
|
models.push(parseToModel(model));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return models;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseToModel = (model) => {
|
||||||
|
const modelVersions = [];
|
||||||
|
model.versions.forEach((v) => {
|
||||||
|
const version = {
|
||||||
|
id: `${model.author}-${v.name}`,
|
||||||
|
name: v.name,
|
||||||
|
quantMethod: v.quantMethod,
|
||||||
|
bits: v.bits,
|
||||||
|
size: v.size,
|
||||||
|
maxRamRequired: v.maxRamRequired,
|
||||||
|
usecase: v.usecase,
|
||||||
|
downloadLink: v.downloadLink,
|
||||||
|
productId: model.id,
|
||||||
|
};
|
||||||
|
modelVersions.push(version);
|
||||||
|
});
|
||||||
|
|
||||||
|
const product = {
|
||||||
|
id: model.id,
|
||||||
|
name: model.name,
|
||||||
|
shortDescription: model.shortDescription,
|
||||||
|
avatarUrl: model.avatarUrl,
|
||||||
|
author: model.author,
|
||||||
|
version: model.version,
|
||||||
|
modelUrl: model.modelUrl,
|
||||||
|
nsfw: model.nsfw,
|
||||||
|
tags: model.tags,
|
||||||
|
greeting: model.defaultGreeting,
|
||||||
|
type: model.type,
|
||||||
|
createdAt: model.createdAt,
|
||||||
|
longDescription: model.longDescription,
|
||||||
|
status: "Downloadable",
|
||||||
|
releaseDate: 0,
|
||||||
|
availableVersions: modelVersions,
|
||||||
|
};
|
||||||
|
return product;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getModelFiles() {
|
||||||
|
const options = {
|
||||||
|
hostname: githubHostName,
|
||||||
|
path: githubPath,
|
||||||
|
headers: githubHeaders,
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = await new Promise((resolve, reject) => {
|
||||||
|
const req = https.request(options, (res) => {
|
||||||
|
let data = "";
|
||||||
|
|
||||||
|
res.on("data", (chunk) => {
|
||||||
|
data += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on("end", () => {
|
||||||
|
const files = JSON.parse(data);
|
||||||
|
|
||||||
|
if (files.filter == null) {
|
||||||
|
console.error(files.message);
|
||||||
|
reject(files.message ?? "No files found");
|
||||||
|
}
|
||||||
|
if (!files || files.length === 0) {
|
||||||
|
resolve([]);
|
||||||
|
}
|
||||||
|
const jsonFiles = files.filter((file) => file.name.endsWith(".json"));
|
||||||
|
resolve(jsonFiles);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on("error", (error) => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getContent(file) {
|
||||||
|
const options = {
|
||||||
|
hostname: githubHostName,
|
||||||
|
path: `${githubPath}/${file.path}`,
|
||||||
|
headers: githubHeaders,
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = await new Promise((resolve) => {
|
||||||
|
const req = https.request(options, (res) => {
|
||||||
|
let data = "";
|
||||||
|
|
||||||
|
res.on("data", (chunk) => {
|
||||||
|
data += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on("end", () => {
|
||||||
|
const fileData = JSON.parse(data);
|
||||||
|
const fileContent = Buffer.from(fileData.content, "base64").toString();
|
||||||
|
resolve(JSON.parse(fileContent));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on("error", (error) => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getDownloadedModels,
|
|
||||||
getAvailableModels,
|
|
||||||
searchModels,
|
searchModels,
|
||||||
|
getConfiguredModels,
|
||||||
};
|
};
|
||||||
|
|||||||
618
package-lock.json
generated
618
package-lock.json
generated
@ -32,22 +32,24 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@npmcli/arborist": "^7.1.0",
|
"@npmcli/arborist": "^7.1.0",
|
||||||
"electron-mocha": "^12.1.0",
|
"@uiball/loaders": "^1.3.0",
|
||||||
"electron-store": "^8.1.0",
|
"electron-store": "^8.1.0",
|
||||||
"electron-updater": "^6.1.4",
|
"electron-updater": "^6.1.4",
|
||||||
"pacote": "^17.0.4",
|
"pacote": "^17.0.4",
|
||||||
|
"react-intersection-observer": "^9.5.2",
|
||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
"request-progress": "^3.0.0"
|
"request-progress": "^3.0.0",
|
||||||
|
"use-debounce": "^9.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@electron/notarize": "^2.1.0",
|
||||||
"@playwright/test": "^1.38.1",
|
"@playwright/test": "^1.38.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.7.3",
|
"@typescript-eslint/eslint-plugin": "^6.7.3",
|
||||||
"@typescript-eslint/parser": "^6.7.3",
|
"@typescript-eslint/parser": "^6.7.3",
|
||||||
"electron": "26.2.1",
|
"electron": "26.2.1",
|
||||||
"electron-builder": "^24.6.4",
|
"electron-builder": "^24.6.4",
|
||||||
"electron-playwright-helpers": "^1.6.0",
|
"electron-playwright-helpers": "^1.6.0",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.33.2"
|
||||||
"xvfb-maybe": "^0.2.1"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aashutoshrathi/word-wrap": {
|
"node_modules/@aashutoshrathi/word-wrap": {
|
||||||
@ -711,6 +713,126 @@
|
|||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@next/swc-darwin-x64": {
|
||||||
|
"version": "13.4.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.10.tgz",
|
||||||
|
"integrity": "sha512-ngXhUBbcZIWZWqNbQSNxQrB9T1V+wgfCzAor2olYuo/YpaL6mUYNUEgeBMhr8qwV0ARSgKaOp35lRvB7EmCRBg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||||
|
"version": "13.4.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.10.tgz",
|
||||||
|
"integrity": "sha512-SjCZZCOmHD4uyM75MVArSAmF5Y+IJSGroPRj2v9/jnBT36SYFTORN8Ag/lhw81W9EeexKY/CUg2e9mdebZOwsg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-linux-arm64-musl": {
|
||||||
|
"version": "13.4.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.10.tgz",
|
||||||
|
"integrity": "sha512-F+VlcWijX5qteoYIOxNiBbNE8ruaWuRlcYyIRK10CugqI/BIeCDzEDyrHIHY8AWwbkTwe6GRHabMdE688Rqq4Q==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-linux-x64-gnu": {
|
||||||
|
"version": "13.4.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.10.tgz",
|
||||||
|
"integrity": "sha512-WDv1YtAV07nhfy3i1visr5p/tjiH6CeXp4wX78lzP1jI07t4PnHHG1WEDFOduXh3WT4hG6yN82EQBQHDi7hBrQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-linux-x64-musl": {
|
||||||
|
"version": "13.4.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.10.tgz",
|
||||||
|
"integrity": "sha512-zFkzqc737xr6qoBgDa3AwC7jPQzGLjDlkNmt/ljvQJ/Veri5ECdHjZCUuiTUfVjshNIIpki6FuP0RaQYK9iCRg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||||
|
"version": "13.4.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.10.tgz",
|
||||||
|
"integrity": "sha512-IboRS8IWz5mWfnjAdCekkl8s0B7ijpWeDwK2O8CdgZkoCDY0ZQHBSGiJ2KViAG6+BJVfLvcP+a2fh6cdyBr9QQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-win32-ia32-msvc": {
|
||||||
|
"version": "13.4.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.10.tgz",
|
||||||
|
"integrity": "sha512-bSA+4j8jY4EEiwD/M2bol4uVEu1lBlgsGdvM+mmBm/BbqofNBfaZ2qwSbwE2OwbAmzNdVJRFRXQZ0dkjopTRaQ==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-win32-x64-msvc": {
|
||||||
|
"version": "13.4.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.10.tgz",
|
||||||
|
"integrity": "sha512-g2+tU63yTWmcVQKDGY0MV1PjjqgZtwM4rB1oVVi/v0brdZAcrcTV+04agKzWtvWroyFz6IqtT0MoZJA7PNyLVw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@ -1666,6 +1788,15 @@
|
|||||||
"url": "https://opencollective.com/typescript-eslint"
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@uiball/loaders": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@uiball/loaders/-/loaders-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-w372e7PMt/s6LZ321HoghgDDU8fomamAzJfrVAdBUhsWERJEpxJMqG37NFztUq/T4J7nzzjkvZI4UX7Z2F/O6A==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.0",
|
||||||
|
"react-dom": ">=16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@xmldom/xmldom": {
|
"node_modules/@xmldom/xmldom": {
|
||||||
"version": "0.8.10",
|
"version": "0.8.10",
|
||||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
|
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
|
||||||
@ -1788,14 +1919,6 @@
|
|||||||
"ajv": "^6.9.1"
|
"ajv": "^6.9.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ansi-colors": {
|
|
||||||
"version": "4.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
|
|
||||||
"integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ansi-regex": {
|
"node_modules/ansi-regex": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
@ -2610,11 +2733,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/browser-stdout": {
|
|
||||||
"version": "1.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
|
|
||||||
"integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="
|
|
||||||
},
|
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.22.1",
|
"version": "4.22.1",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz",
|
||||||
@ -2856,17 +2974,6 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/camelcase": {
|
|
||||||
"version": "6.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
|
|
||||||
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/camelcase-css": {
|
"node_modules/camelcase-css": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
||||||
@ -3104,6 +3211,7 @@
|
|||||||
"version": "8.0.1",
|
"version": "8.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||||
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"string-width": "^4.2.0",
|
"string-width": "^4.2.0",
|
||||||
@ -3607,17 +3715,6 @@
|
|||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/decamelize": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/decode-named-character-reference": {
|
"node_modules/decode-named-character-reference": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz",
|
||||||
@ -4070,38 +4167,6 @@
|
|||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron-mocha": {
|
|
||||||
"version": "12.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/electron-mocha/-/electron-mocha-12.1.0.tgz",
|
|
||||||
"integrity": "sha512-9ZIvyHGbet4ZtvF2NYYjGm7/yPljnTxbHo8psVX/HQAFPp9vZE0mCNWlzwE42keq/42gBW6W40MtKmgn1v42hQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-colors": "^4.1.1",
|
|
||||||
"electron-window": "^0.8.0",
|
|
||||||
"mocha": "^10.2.0",
|
|
||||||
"which": "^3.0.0",
|
|
||||||
"yargs": "^17.7.2"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"electron-mocha": "bin/electron-mocha"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 16.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/electron-mocha/node_modules/which": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==",
|
|
||||||
"dependencies": {
|
|
||||||
"isexe": "^2.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"node-which": "bin/which.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/electron-playwright-helpers": {
|
"node_modules/electron-playwright-helpers": {
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/electron-playwright-helpers/-/electron-playwright-helpers-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/electron-playwright-helpers/-/electron-playwright-helpers-1.6.0.tgz",
|
||||||
@ -4163,14 +4228,6 @@
|
|||||||
"tiny-typed-emitter": "^2.1.0"
|
"tiny-typed-emitter": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron-window": {
|
|
||||||
"version": "0.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/electron-window/-/electron-window-0.8.1.tgz",
|
|
||||||
"integrity": "sha512-W1i9LfnZJozk3MXE8VgsL2E5wOUHSgyCvcg1H2vQQjj+gqhO9lVudgY3z3SF7LJAmi+0vy3CJkbMqsynWB49EA==",
|
|
||||||
"dependencies": {
|
|
||||||
"is-electron-renderer": "^2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/electron/node_modules/@types/node": {
|
"node_modules/electron/node_modules/@types/node": {
|
||||||
"version": "18.18.3",
|
"version": "18.18.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.3.tgz",
|
||||||
@ -5250,14 +5307,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/flat": {
|
|
||||||
"version": "5.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
|
|
||||||
"integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
|
|
||||||
"bin": {
|
|
||||||
"flat": "cli.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/flat-cache": {
|
"node_modules/flat-cache": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz",
|
||||||
@ -5512,6 +5561,7 @@
|
|||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||||
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "6.* || 8.* || >= 10.*"
|
"node": "6.* || 8.* || >= 10.*"
|
||||||
@ -6175,14 +6225,6 @@
|
|||||||
"url": "https://github.com/sponsors/wooorm"
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/he": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
|
|
||||||
"bin": {
|
|
||||||
"he": "bin/he"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/highlight.js": {
|
"node_modules/highlight.js": {
|
||||||
"version": "10.7.3",
|
"version": "10.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
|
||||||
@ -6710,11 +6752,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-electron-renderer": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-electron-renderer/-/is-electron-renderer-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-pRlQnpaCFhDVPtkXkP+g9Ybv/CjbiQDjnKFQTEjpBfDKeV6dRDBczuFRDpM6DVfk2EjpMS8t5kwE5jPnqYl3zA=="
|
|
||||||
},
|
|
||||||
"node_modules/is-equal-shallow": {
|
"node_modules/is-equal-shallow": {
|
||||||
"version": "0.1.3",
|
"version": "0.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz",
|
||||||
@ -7021,17 +7058,6 @@
|
|||||||
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
|
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/is-unicode-supported": {
|
|
||||||
"version": "0.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
|
|
||||||
"integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/is-weakmap": {
|
"node_modules/is-weakmap": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
|
||||||
@ -7540,21 +7566,6 @@
|
|||||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/log-symbols": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
|
|
||||||
"dependencies": {
|
|
||||||
"chalk": "^4.1.0",
|
|
||||||
"is-unicode-supported": "^0.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/longest-streak": {
|
"node_modules/longest-streak": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
|
||||||
@ -8552,249 +8563,6 @@
|
|||||||
"mkdirp": "bin/cmd.js"
|
"mkdirp": "bin/cmd.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mocha": {
|
|
||||||
"version": "10.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz",
|
|
||||||
"integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-colors": "4.1.1",
|
|
||||||
"browser-stdout": "1.3.1",
|
|
||||||
"chokidar": "3.5.3",
|
|
||||||
"debug": "4.3.4",
|
|
||||||
"diff": "5.0.0",
|
|
||||||
"escape-string-regexp": "4.0.0",
|
|
||||||
"find-up": "5.0.0",
|
|
||||||
"glob": "7.2.0",
|
|
||||||
"he": "1.2.0",
|
|
||||||
"js-yaml": "4.1.0",
|
|
||||||
"log-symbols": "4.1.0",
|
|
||||||
"minimatch": "5.0.1",
|
|
||||||
"ms": "2.1.3",
|
|
||||||
"nanoid": "3.3.3",
|
|
||||||
"serialize-javascript": "6.0.0",
|
|
||||||
"strip-json-comments": "3.1.1",
|
|
||||||
"supports-color": "8.1.1",
|
|
||||||
"workerpool": "6.2.1",
|
|
||||||
"yargs": "16.2.0",
|
|
||||||
"yargs-parser": "20.2.4",
|
|
||||||
"yargs-unparser": "2.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"_mocha": "bin/_mocha",
|
|
||||||
"mocha": "bin/mocha.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 14.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/mochajs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/ansi-colors": {
|
|
||||||
"version": "4.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
|
|
||||||
"integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/anymatch": {
|
|
||||||
"version": "3.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
|
||||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
|
||||||
"dependencies": {
|
|
||||||
"normalize-path": "^3.0.0",
|
|
||||||
"picomatch": "^2.0.4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/binary-extensions": {
|
|
||||||
"version": "2.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
|
||||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/chokidar": {
|
|
||||||
"version": "3.5.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
|
||||||
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://paulmillr.com/funding/"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"anymatch": "~3.1.2",
|
|
||||||
"braces": "~3.0.2",
|
|
||||||
"glob-parent": "~5.1.2",
|
|
||||||
"is-binary-path": "~2.1.0",
|
|
||||||
"is-glob": "~4.0.1",
|
|
||||||
"normalize-path": "~3.0.0",
|
|
||||||
"readdirp": "~3.6.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 8.10.0"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"fsevents": "~2.3.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/cliui": {
|
|
||||||
"version": "7.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
|
||||||
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"string-width": "^4.2.0",
|
|
||||||
"strip-ansi": "^6.0.0",
|
|
||||||
"wrap-ansi": "^7.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/diff": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.3.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/fsevents": {
|
|
||||||
"version": "2.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
|
||||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/glob": {
|
|
||||||
"version": "7.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
|
|
||||||
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
|
|
||||||
"dependencies": {
|
|
||||||
"fs.realpath": "^1.0.0",
|
|
||||||
"inflight": "^1.0.4",
|
|
||||||
"inherits": "2",
|
|
||||||
"minimatch": "^3.0.4",
|
|
||||||
"once": "^1.3.0",
|
|
||||||
"path-is-absolute": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/glob-parent": {
|
|
||||||
"version": "5.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
|
||||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
|
||||||
"dependencies": {
|
|
||||||
"is-glob": "^4.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/glob/node_modules/brace-expansion": {
|
|
||||||
"version": "1.1.11",
|
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
|
||||||
"dependencies": {
|
|
||||||
"balanced-match": "^1.0.0",
|
|
||||||
"concat-map": "0.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/glob/node_modules/minimatch": {
|
|
||||||
"version": "3.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
|
||||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
|
||||||
"dependencies": {
|
|
||||||
"brace-expansion": "^1.1.7"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/is-binary-path": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
|
||||||
"dependencies": {
|
|
||||||
"binary-extensions": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/minimatch": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
|
|
||||||
"dependencies": {
|
|
||||||
"brace-expansion": "^2.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/nanoid": {
|
|
||||||
"version": "3.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
|
|
||||||
"integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==",
|
|
||||||
"bin": {
|
|
||||||
"nanoid": "bin/nanoid.cjs"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/readdirp": {
|
|
||||||
"version": "3.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
|
||||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
|
||||||
"dependencies": {
|
|
||||||
"picomatch": "^2.2.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/yargs": {
|
|
||||||
"version": "16.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
|
||||||
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
|
|
||||||
"dependencies": {
|
|
||||||
"cliui": "^7.0.2",
|
|
||||||
"escalade": "^3.1.1",
|
|
||||||
"get-caller-file": "^2.0.5",
|
|
||||||
"require-directory": "^2.1.1",
|
|
||||||
"string-width": "^4.2.0",
|
|
||||||
"y18n": "^5.0.5",
|
|
||||||
"yargs-parser": "^20.2.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mocha/node_modules/yargs-parser": {
|
|
||||||
"version": "20.2.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
|
|
||||||
"integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mri": {
|
"node_modules/mri": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
||||||
@ -10637,14 +10405,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/randombytes": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"safe-buffer": "^5.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react": {
|
"node_modules/react": {
|
||||||
"version": "18.2.0",
|
"version": "18.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||||
@ -10686,6 +10446,14 @@
|
|||||||
"react": "^16.8.0 || ^17 || ^18"
|
"react": "^16.8.0 || ^17 || ^18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-intersection-observer": {
|
||||||
|
"version": "9.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.5.2.tgz",
|
||||||
|
"integrity": "sha512-EmoV66/yvksJcGa1rdW0nDNc4I1RifDWkT50gXSFnPLYQ4xUptuDD4V7k+Rj1OgVAlww628KLGcxPXFlOkkU/Q==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
@ -11476,6 +11244,7 @@
|
|||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@ -11845,14 +11614,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/serialize-javascript": {
|
|
||||||
"version": "6.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
|
|
||||||
"integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
|
|
||||||
"dependencies": {
|
|
||||||
"randombytes": "^2.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/set-blocking": {
|
"node_modules/set-blocking": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||||
@ -12606,6 +12367,7 @@
|
|||||||
"version": "8.1.1",
|
"version": "8.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
|
||||||
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
|
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"has-flag": "^4.0.0"
|
"has-flag": "^4.0.0"
|
||||||
@ -13569,6 +13331,17 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-debounce": {
|
||||||
|
"version": "9.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-9.0.4.tgz",
|
||||||
|
"integrity": "sha512-6X8H/mikbrt0XE8e+JXRtZ8yYVvKkdYRfmIhWZYsP8rcNs9hk3APV8Ua2mFkKRLcJKVdnX2/Vwrmg2GWKUQEaQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/utf8-byte-length": {
|
"node_modules/utf8-byte-length": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz",
|
||||||
@ -13876,15 +13649,11 @@
|
|||||||
"string-width": "^1.0.2 || 2 || 3 || 4"
|
"string-width": "^1.0.2 || 2 || 3 || 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/workerpool": {
|
|
||||||
"version": "6.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
|
|
||||||
"integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw=="
|
|
||||||
},
|
|
||||||
"node_modules/wrap-ansi": {
|
"node_modules/wrap-ansi": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-styles": "^4.0.0",
|
"ansi-styles": "^4.0.0",
|
||||||
@ -13954,54 +13723,11 @@
|
|||||||
"node": ">=0.4"
|
"node": ">=0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/xvfb-maybe": {
|
|
||||||
"version": "0.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/xvfb-maybe/-/xvfb-maybe-0.2.1.tgz",
|
|
||||||
"integrity": "sha512-9IyRz3l6Qyhl6LvnGRF5jMPB4oBEepQnuzvVAFTynP6ACLLSevqigICJ9d/+ofl29m2daeaVBChnPYUnaeJ7yA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"debug": "^2.2.0",
|
|
||||||
"which": "^1.2.4"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"xvfb-maybe": "src/xvfb-maybe.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/xvfb-maybe/node_modules/debug": {
|
|
||||||
"version": "2.6.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
|
||||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ms": "2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/xvfb-maybe/node_modules/ms": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/xvfb-maybe/node_modules/which": {
|
|
||||||
"version": "1.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
|
|
||||||
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"isexe": "^2.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"which": "bin/which"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/y18n": {
|
"node_modules/y18n": {
|
||||||
"version": "5.0.8",
|
"version": "5.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||||
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
@ -14026,6 +13752,7 @@
|
|||||||
"version": "17.7.2",
|
"version": "17.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cliui": "^8.0.1",
|
"cliui": "^8.0.1",
|
||||||
@ -14044,33 +13771,12 @@
|
|||||||
"version": "21.1.1",
|
"version": "21.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
||||||
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/yargs-unparser": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
|
|
||||||
"dependencies": {
|
|
||||||
"camelcase": "^6.0.0",
|
|
||||||
"decamelize": "^4.0.0",
|
|
||||||
"flat": "^5.0.2",
|
|
||||||
"is-plain-obj": "^2.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/yargs-unparser/node_modules/is-plain-obj": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/yauzl": {
|
"node_modules/yauzl": {
|
||||||
"version": "2.10.0",
|
"version": "2.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import React, { Fragment } from "react";
|
import React from "react";
|
||||||
import ModelTable from "../ModelTable";
|
import ModelTable from "../ModelTable";
|
||||||
import { currentProductAtom } from "@/_helpers/atoms/Model.atom";
|
import { activeAssistantModelAtom } from "@/_helpers/atoms/Model.atom";
|
||||||
|
|
||||||
const ActiveModelTable: React.FC = () => {
|
const ActiveModelTable: React.FC = () => {
|
||||||
const activeModel = useAtomValue(currentProductAtom);
|
const activeModel = useAtomValue(activeAssistantModelAtom);
|
||||||
|
|
||||||
if (!activeModel) return null;
|
if (!activeModel) return null;
|
||||||
|
|
||||||
|
|||||||
@ -1,19 +1,19 @@
|
|||||||
import { Product } from "@/_models/Product";
|
|
||||||
import DownloadModelContent from "../DownloadModelContent";
|
import DownloadModelContent from "../DownloadModelContent";
|
||||||
import ModelDownloadButton from "../ModelDownloadButton";
|
import ModelDownloadButton from "../ModelDownloadButton";
|
||||||
import ModelDownloadingButton from "../ModelDownloadingButton";
|
import ModelDownloadingButton from "../ModelDownloadingButton";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { modelDownloadStateAtom } from "@/_helpers/atoms/DownloadState.atom";
|
import { modelDownloadStateAtom } from "@/_helpers/atoms/DownloadState.atom";
|
||||||
|
import { AssistantModel } from "@/_models/AssistantModel";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
product: Product;
|
model: AssistantModel;
|
||||||
isRecommend: boolean;
|
isRecommend: boolean;
|
||||||
required?: string;
|
required?: string;
|
||||||
onDownloadClick?: (product: Product) => void;
|
onDownloadClick?: (model: AssistantModel) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AvailableModelCard: React.FC<Props> = ({
|
const AvailableModelCard: React.FC<Props> = ({
|
||||||
product,
|
model,
|
||||||
isRecommend,
|
isRecommend,
|
||||||
required,
|
required,
|
||||||
onDownloadClick,
|
onDownloadClick,
|
||||||
@ -24,14 +24,14 @@ const AvailableModelCard: React.FC<Props> = ({
|
|||||||
let total = 0;
|
let total = 0;
|
||||||
let transferred = 0;
|
let transferred = 0;
|
||||||
|
|
||||||
if (product.fileName && downloadState[product.fileName]) {
|
if (model.id && downloadState[model.id]) {
|
||||||
isDownloading =
|
isDownloading =
|
||||||
downloadState[product.fileName].error == null &&
|
downloadState[model.id].error == null &&
|
||||||
downloadState[product.fileName].percent < 1;
|
downloadState[model.id].percent < 1;
|
||||||
|
|
||||||
if (isDownloading) {
|
if (isDownloading) {
|
||||||
total = downloadState[product.fileName].size.total;
|
total = downloadState[model.id].size.total;
|
||||||
transferred = downloadState[product.fileName].size.transferred;
|
transferred = downloadState[model.id].size.transferred;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ const AvailableModelCard: React.FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-1/5 flex items-center justify-end">
|
<div className="w-1/5 flex items-center justify-end">
|
||||||
<ModelDownloadButton callback={() => onDownloadClick?.(product)} />
|
<ModelDownloadButton callback={() => onDownloadClick?.(model)} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -50,11 +50,11 @@ const AvailableModelCard: React.FC<Props> = ({
|
|||||||
<div className="flex justify-between py-4 px-3 gap-2.5">
|
<div className="flex justify-between py-4 px-3 gap-2.5">
|
||||||
<DownloadModelContent
|
<DownloadModelContent
|
||||||
required={required}
|
required={required}
|
||||||
author={product.author}
|
author={model.author}
|
||||||
description={product.description}
|
description={model.shortDescription}
|
||||||
isRecommend={isRecommend}
|
isRecommend={isRecommend}
|
||||||
name={product.name}
|
name={model.name}
|
||||||
type={product.type}
|
type={model.type}
|
||||||
/>
|
/>
|
||||||
{downloadButton}
|
{downloadButton}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import useCreateConversation from "@/_hooks/useCreateConversation";
|
import useCreateConversation from "@/_hooks/useCreateConversation";
|
||||||
import { Product } from "@/_models/Product";
|
import { AssistantModel } from "@/_models/AssistantModel";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
product: Product;
|
model: AssistantModel;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ConversationalCard: React.FC<Props> = ({ product }) => {
|
const ConversationalCard: React.FC<Props> = ({ model }) => {
|
||||||
const { requestCreateConvo } = useCreateConversation();
|
const { requestCreateConvo } = useCreateConversation();
|
||||||
|
|
||||||
const { name, avatarUrl, description } = product;
|
const { name, avatarUrl, shortDescription } = model;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={() => requestCreateConvo(product)}
|
onClick={() => requestCreateConvo(model)}
|
||||||
className="flex flex-col justify-between flex-shrink-0 gap-3 bg-white p-4 w-52 rounded-lg text-left dark:bg-gray-700 hover:opacity-20"
|
className="flex flex-col justify-between flex-shrink-0 gap-3 bg-white p-4 w-52 rounded-lg text-left dark:bg-gray-700 hover:opacity-20"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-2 box-border">
|
<div className="flex flex-col gap-2 box-border">
|
||||||
@ -29,7 +29,7 @@ const ConversationalCard: React.FC<Props> = ({ product }) => {
|
|||||||
{name}
|
{name}
|
||||||
</h2>
|
</h2>
|
||||||
<span className="text-gray-600 mt-1 font-normal line-clamp-2">
|
<span className="text-gray-600 mt-1 font-normal line-clamp-2">
|
||||||
{description}
|
{shortDescription}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="flex text-xs leading-5 text-gray-500 items-center gap-0.5">
|
<span className="flex text-xs leading-5 text-gray-500 items-center gap-0.5">
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import { Product } from "@/_models/Product";
|
import { AssistantModel } from "@/_models/AssistantModel";
|
||||||
import ConversationalCard from "../ConversationalCard";
|
import ConversationalCard from "../ConversationalCard";
|
||||||
import { ChatBubbleBottomCenterTextIcon } from "@heroicons/react/24/outline";
|
import { ChatBubbleBottomCenterTextIcon } from "@heroicons/react/24/outline";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
products: Product[];
|
models: AssistantModel[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const ConversationalList: React.FC<Props> = ({ products }) => (
|
const ConversationalList: React.FC<Props> = ({ models }) => (
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center gap-3 mt-8 mb-2">
|
<div className="flex items-center gap-3 mt-8 mb-2">
|
||||||
<ChatBubbleBottomCenterTextIcon width={24} height={24} className="ml-6" />
|
<ChatBubbleBottomCenterTextIcon width={24} height={24} className="ml-6" />
|
||||||
@ -15,8 +15,8 @@ const ConversationalList: React.FC<Props> = ({ products }) => (
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 pl-6 flex w-full gap-2 overflow-x-scroll scroll overflow-hidden">
|
<div className="mt-2 pl-6 flex w-full gap-2 overflow-x-scroll scroll overflow-hidden">
|
||||||
{products.map((item) => (
|
{models.map((item) => (
|
||||||
<ConversationalCard key={item.slug} product={item} />
|
<ConversationalCard key={item.id} model={item} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import { Product } from "@/_models/Product";
|
import { AssistantModel } from "@/_models/AssistantModel";
|
||||||
import DownloadModelContent from "../DownloadModelContent";
|
import DownloadModelContent from "../DownloadModelContent";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
product: Product;
|
model: AssistantModel;
|
||||||
isRecommend: boolean;
|
isRecommend: boolean;
|
||||||
required?: string;
|
required?: string;
|
||||||
transferred?: number;
|
transferred?: number;
|
||||||
onDeleteClick?: (product: Product) => void;
|
onDeleteClick?: (model: AssistantModel) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DownloadedModelCard: React.FC<Props> = ({
|
const DownloadedModelCard: React.FC<Props> = ({
|
||||||
product,
|
model,
|
||||||
isRecommend,
|
isRecommend,
|
||||||
required,
|
required,
|
||||||
onDeleteClick,
|
onDeleteClick,
|
||||||
@ -19,14 +19,14 @@ const DownloadedModelCard: React.FC<Props> = ({
|
|||||||
<div className="flex justify-between py-4 px-3 gap-2.5">
|
<div className="flex justify-between py-4 px-3 gap-2.5">
|
||||||
<DownloadModelContent
|
<DownloadModelContent
|
||||||
required={required}
|
required={required}
|
||||||
author={product.author}
|
author={model.author}
|
||||||
description={product.description}
|
description={model.shortDescription}
|
||||||
isRecommend={isRecommend}
|
isRecommend={isRecommend}
|
||||||
name={product.name}
|
name={model.name}
|
||||||
type={product.type}
|
type={model.type}
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col justify-center">
|
<div className="flex flex-col justify-center">
|
||||||
<button onClick={() => onDeleteClick?.(product)}>Delete</button>
|
<button onClick={() => onDeleteClick?.(model)}>Delete</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -81,7 +81,7 @@ const ExploreModelItem = forwardRef<HTMLDivElement, Props>(({ model }, ref) => {
|
|||||||
<span className="text-sm font-medium text-gray-500">Tags</span>
|
<span className="text-sm font-medium text-gray-500">Tags</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{model.availableVersions.length > 0 && (
|
{model.availableVersions?.length > 0 && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{show && (
|
{show && (
|
||||||
<ModelVersionList
|
<ModelVersionList
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import PrimaryButton from "../PrimaryButton";
|
|||||||
import { formatDownloadPercentage, toGigabytes } from "@/_utils/converter";
|
import { formatDownloadPercentage, toGigabytes } from "@/_utils/converter";
|
||||||
import { DownloadState } from "@/_models/DownloadState";
|
import { DownloadState } from "@/_models/DownloadState";
|
||||||
import SecondaryButton from "../SecondaryButton";
|
import SecondaryButton from "../SecondaryButton";
|
||||||
import { ModelVersion } from "@/_models/Product";
|
import { ModelVersion } from "@/_models/ModelVersion";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@ -1,50 +1,20 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import ExploreModelItem from "../ExploreModelItem";
|
import ExploreModelItem from "../ExploreModelItem";
|
||||||
import { modelSearchAtom } from "@/_helpers/JotaiWrapper";
|
import { getConfiguredModels } from "@/_hooks/useGetDownloadedModels";
|
||||||
import useGetHuggingFaceModel from "@/_hooks/useGetHuggingFaceModel";
|
import useGetConfiguredModels from "@/_hooks/useGetConfiguredModels";
|
||||||
import { useAtom, useAtomValue } from "jotai";
|
|
||||||
import { useInView } from "react-intersection-observer";
|
|
||||||
import { modelLoadMoreAtom } from "@/_helpers/atoms/ExploreModelLoading.atom";
|
|
||||||
import { Waveform } from "@uiball/loaders";
|
|
||||||
|
|
||||||
const ExploreModelList: React.FC = () => {
|
const ExploreModelList: React.FC = () => {
|
||||||
const [loadMoreInProgress, setLoadMoreInProress] = useAtom(modelLoadMoreAtom);
|
const { models } = useGetConfiguredModels();
|
||||||
const modelSearch = useAtomValue(modelSearchAtom);
|
|
||||||
const { modelList, getHuggingFaceModel } = useGetHuggingFaceModel();
|
|
||||||
const { ref, inView } = useInView({
|
|
||||||
threshold: 0,
|
|
||||||
triggerOnce: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (modelList.length === 0 && modelSearch.length > 0) {
|
getConfiguredModels();
|
||||||
setLoadMoreInProress(true);
|
}, []);
|
||||||
}
|
|
||||||
getHuggingFaceModel(modelSearch);
|
|
||||||
}, [modelSearch]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (inView) {
|
|
||||||
console.debug("Load more models..");
|
|
||||||
setLoadMoreInProress(true);
|
|
||||||
getHuggingFaceModel(modelSearch);
|
|
||||||
}
|
|
||||||
}, [inView]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col flex-1 overflow-y-auto scroll">
|
<div className="flex flex-col flex-1 overflow-y-auto scroll">
|
||||||
{modelList.map((item, index) => (
|
{models.map((item) => (
|
||||||
<ExploreModelItem
|
<ExploreModelItem key={item.id} model={item} />
|
||||||
ref={index === modelList.length - 1 ? ref : null}
|
|
||||||
key={item.id}
|
|
||||||
model={item}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
{loadMoreInProgress && (
|
|
||||||
<div className="mx-auto mt-2 mb-4">
|
|
||||||
<Waveform size={24} color="#9CA3AF" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,13 +8,13 @@ import SecondaryButton from "../SecondaryButton";
|
|||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import { PlusIcon } from "@heroicons/react/24/outline";
|
import { PlusIcon } from "@heroicons/react/24/outline";
|
||||||
import useCreateConversation from "@/_hooks/useCreateConversation";
|
import useCreateConversation from "@/_hooks/useCreateConversation";
|
||||||
import { currentProductAtom } from "@/_helpers/atoms/Model.atom";
|
import { activeAssistantModelAtom } from "@/_helpers/atoms/Model.atom";
|
||||||
import LoadingIndicator from "../LoadingIndicator";
|
import LoadingIndicator from "../LoadingIndicator";
|
||||||
import { currentConvoStateAtom } from "@/_helpers/atoms/Conversation.atom";
|
import { currentConvoStateAtom } from "@/_helpers/atoms/Conversation.atom";
|
||||||
|
|
||||||
const InputToolbar: React.FC = () => {
|
const InputToolbar: React.FC = () => {
|
||||||
const showingAdvancedPrompt = useAtomValue(showingAdvancedPromptAtom);
|
const showingAdvancedPrompt = useAtomValue(showingAdvancedPromptAtom);
|
||||||
const currentProduct = useAtomValue(currentProductAtom);
|
const activeModel = useAtomValue(activeAssistantModelAtom);
|
||||||
const { requestCreateConvo } = useCreateConversation();
|
const { requestCreateConvo } = useCreateConversation();
|
||||||
const currentConvoState = useAtomValue(currentConvoStateAtom);
|
const currentConvoState = useAtomValue(currentConvoStateAtom);
|
||||||
|
|
||||||
@ -26,8 +26,8 @@ const InputToolbar: React.FC = () => {
|
|||||||
// const onRegenerateClick = () => {};
|
// const onRegenerateClick = () => {};
|
||||||
|
|
||||||
const onNewConversationClick = () => {
|
const onNewConversationClick = () => {
|
||||||
if (currentProduct) {
|
if (activeModel) {
|
||||||
requestCreateConvo(currentProduct);
|
requestCreateConvo(activeModel);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -49,7 +49,6 @@ const InputToolbar: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* <SecondaryButton title="Regenerate" onClick={onRegenerateClick} /> */}
|
{/* <SecondaryButton title="Regenerate" onClick={onRegenerateClick} /> */}
|
||||||
<SecondaryButton
|
<SecondaryButton
|
||||||
onClick={onNewConversationClick}
|
onClick={onNewConversationClick}
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
import React from "react";
|
import React, { useCallback } from "react";
|
||||||
import { Product } from "@/_models/Product";
|
|
||||||
import Image from "next/image";
|
|
||||||
import { ModelStatus, ModelStatusComponent } from "../ModelStatusComponent";
|
import { ModelStatus, ModelStatusComponent } from "../ModelStatusComponent";
|
||||||
import ModelActionMenu from "../ModelActionMenu";
|
import ModelActionMenu from "../ModelActionMenu";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import ModelActionButton, { ModelActionType } from "../ModelActionButton";
|
import ModelActionButton, { ModelActionType } from "../ModelActionButton";
|
||||||
import useStartStopModel from "@/_hooks/useStartStopModel";
|
import useStartStopModel from "@/_hooks/useStartStopModel";
|
||||||
import useDeleteModel from "@/_hooks/useDeleteModel";
|
import useDeleteModel from "@/_hooks/useDeleteModel";
|
||||||
import { currentProductAtom } from "@/_helpers/atoms/Model.atom";
|
import { AssistantModel } from "@/_models/AssistantModel";
|
||||||
|
import { activeAssistantModelAtom } from "@/_helpers/atoms/Model.atom";
|
||||||
|
import { toGigabytes } from "@/_utils/converter";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
model: Product;
|
model: AssistantModel;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ModelRow: React.FC<Props> = ({ model }) => {
|
const ModelRow: React.FC<Props> = ({ model }) => {
|
||||||
const { startModel, stopModel } = useStartStopModel();
|
const { startModel, stopModel } = useStartStopModel();
|
||||||
const activeModel = useAtomValue(currentProductAtom);
|
const activeModel = useAtomValue(activeAssistantModelAtom);
|
||||||
const { deleteModel } = useDeleteModel();
|
const { deleteModel } = useDeleteModel();
|
||||||
|
|
||||||
let status = ModelStatus.Installed;
|
let status = ModelStatus.Installed;
|
||||||
@ -36,32 +36,23 @@ const ModelRow: React.FC<Props> = ({ model }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDeleteClick = () => {
|
const onDeleteClick = useCallback(() => {
|
||||||
deleteModel(model);
|
deleteModel(model);
|
||||||
};
|
}, [model]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr
|
<tr className="border-b border-gray-200 last:border-b-0 last:rounded-lg">
|
||||||
className="border-b border-gray-200 last:border-b-0 last:rounded-lg"
|
|
||||||
key={model.id}
|
|
||||||
>
|
|
||||||
<td className="flex flex-col whitespace-nowrap px-6 py-4 text-sm font-medium text-gray-900">
|
<td className="flex flex-col whitespace-nowrap px-6 py-4 text-sm font-medium text-gray-900">
|
||||||
{model.name}
|
{model.name}
|
||||||
<span className="text-gray-500 font-normal">{model.version}</span>
|
<span className="text-gray-500 font-normal">{model.version}</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="whitespace-nowrap px-6 py-4 text-sm text-gray-500">
|
<td className="whitespace-nowrap px-6 py-4 text-sm text-gray-500">
|
||||||
<div className="flex flex-col justify-start">
|
<div className="flex flex-col justify-start">
|
||||||
<span>{model.format}</span>
|
<span>GGUF</span>
|
||||||
{model.accelerated && (
|
|
||||||
<span className="flex items-center text-gray-500 text-sm font-normal gap-0.5">
|
|
||||||
<Image src={"/icons/flash.svg"} width={20} height={20} alt="" />
|
|
||||||
GPU Accelerated
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="whitespace-nowrap px-6 py-4 text-sm text-gray-500">
|
<td className="whitespace-nowrap px-6 py-4 text-sm text-gray-500">
|
||||||
{model.totalSize}
|
{toGigabytes(model.size)}
|
||||||
</td>
|
</td>
|
||||||
<td className="whitespace-nowrap px-6 py-4 text-sm text-gray-500">
|
<td className="whitespace-nowrap px-6 py-4 text-sm text-gray-500">
|
||||||
<ModelStatusComponent status={status} />
|
<ModelStatusComponent status={status} />
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { Fragment, useEffect } from "react";
|
import { Fragment, useEffect } from "react";
|
||||||
import { Listbox, Transition } from "@headlessui/react";
|
import { Listbox, Transition } from "@headlessui/react";
|
||||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/react/20/solid";
|
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/react/20/solid";
|
||||||
import { Product } from "@/_models/Product";
|
|
||||||
import { useAtom, useAtomValue } from "jotai";
|
import { useAtom, useAtomValue } from "jotai";
|
||||||
import { selectedModelAtom } from "@/_helpers/atoms/Model.atom";
|
import { selectedModelAtom } from "@/_helpers/atoms/Model.atom";
|
||||||
import { downloadedModelAtom } from "@/_helpers/atoms/DownloadedModel.atom";
|
import { downloadedModelAtom } from "@/_helpers/atoms/DownloadedModel.atom";
|
||||||
|
import { AssistantModel } from "@/_models/AssistantModel";
|
||||||
|
|
||||||
function classNames(...classes: any) {
|
function classNames(...classes: any) {
|
||||||
return classes.filter(Boolean).join(" ");
|
return classes.filter(Boolean).join(" ");
|
||||||
@ -20,7 +20,7 @@ const SelectModels: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}, [downloadedModels]);
|
}, [downloadedModels]);
|
||||||
|
|
||||||
const onModelSelected = (model: Product) => {
|
const onModelSelected = (model: AssistantModel) => {
|
||||||
setSelectedModel(model);
|
setSelectedModel(model);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Product } from "@/_models/Product";
|
|
||||||
import ModelRow from "../ModelRow";
|
import ModelRow from "../ModelRow";
|
||||||
import ModelTableHeader from "../ModelTableHeader";
|
import ModelTableHeader from "../ModelTableHeader";
|
||||||
|
import { AssistantModel } from "@/_models/AssistantModel";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
models: Product[];
|
models: AssistantModel[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const tableHeaders = ["MODEL", "FORMAT", "SIZE", "STATUS", "ACTIONS"];
|
const tableHeaders = ["MODEL", "FORMAT", "SIZE", "STATUS", "ACTIONS"];
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
import { formatDownloadPercentage, toGigabytes } from "@/_utils/converter";
|
import { formatDownloadPercentage, toGigabytes } from "@/_utils/converter";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { ModelVersion, Product } from "@/_models/Product";
|
import { Product } from "@/_models/Product";
|
||||||
import useDownloadModel from "@/_hooks/useDownloadModel";
|
import useDownloadModel from "@/_hooks/useDownloadModel";
|
||||||
import { modelDownloadStateAtom } from "@/_helpers/atoms/DownloadState.atom";
|
import { modelDownloadStateAtom } from "@/_helpers/atoms/DownloadState.atom";
|
||||||
import { atom, useAtomValue } from "jotai";
|
import { atom, useAtomValue } from "jotai";
|
||||||
|
import { ModelVersion } from "@/_models/ModelVersion";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
model: Product;
|
model: Product;
|
||||||
@ -12,15 +13,15 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ModelVersionItem: React.FC<Props> = ({ model, modelVersion }) => {
|
const ModelVersionItem: React.FC<Props> = ({ model, modelVersion }) => {
|
||||||
const { downloadHfModel } = useDownloadModel();
|
const { downloadModel } = useDownloadModel();
|
||||||
const downloadAtom = useMemo(
|
const downloadAtom = useMemo(
|
||||||
() => atom((get) => get(modelDownloadStateAtom)[modelVersion.path ?? ""]),
|
() => atom((get) => get(modelDownloadStateAtom)[modelVersion.id ?? ""]),
|
||||||
[modelVersion.path ?? ""]
|
[modelVersion.id ?? ""]
|
||||||
);
|
);
|
||||||
const downloadState = useAtomValue(downloadAtom);
|
const downloadState = useAtomValue(downloadAtom);
|
||||||
|
|
||||||
const onDownloadClick = () => {
|
const onDownloadClick = () => {
|
||||||
downloadHfModel(model, modelVersion);
|
downloadModel(model, modelVersion);
|
||||||
};
|
};
|
||||||
|
|
||||||
let downloadButton = (
|
let downloadButton = (
|
||||||
@ -42,7 +43,7 @@ const ModelVersionItem: React.FC<Props> = ({ model, modelVersion }) => {
|
|||||||
<div className="flex justify-between items-center gap-4 pl-3 pt-3 pr-4 pb-3 border-t border-gray-200 first:border-t-0">
|
<div className="flex justify-between items-center gap-4 pl-3 pt-3 pr-4 pb-3 border-t border-gray-200 first:border-t-0">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<Image src={"/icons/app_icon.svg"} width={14} height={20} alt="" />
|
<Image src={"/icons/app_icon.svg"} width={14} height={20} alt="" />
|
||||||
<span className="font-sm text-gray-900">{modelVersion.path}</span>
|
<span className="font-sm text-gray-900">{modelVersion.name}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<div className="px-2.5 py-0.5 bg-gray-200 text-xs font-medium rounded">
|
<div className="px-2.5 py-0.5 bg-gray-200 text-xs font-medium rounded">
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import ModelVersionItem from "../ModelVersionItem";
|
import ModelVersionItem from "../ModelVersionItem";
|
||||||
import { ModelVersion, Product } from "@/_models/Product";
|
import { Product } from "@/_models/Product";
|
||||||
|
import { ModelVersion } from "@/_models/ModelVersion";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
model: Product;
|
model: Product;
|
||||||
@ -12,7 +13,7 @@ const ModelVersionList: React.FC<Props> = ({ model, versions }) => (
|
|||||||
<div className="text-sm font-medium text-gray-500">Available Versions</div>
|
<div className="text-sm font-medium text-gray-500">Available Versions</div>
|
||||||
<div className="border border-gray-200 rounded-lg overflow-hidden">
|
<div className="border border-gray-200 rounded-lg overflow-hidden">
|
||||||
{versions.map((item) => (
|
{versions.map((item) => (
|
||||||
<ModelVersionItem key={item.path} model={model} modelVersion={item} />
|
<ModelVersionItem key={item.id} model={model} modelVersion={item} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,16 +2,16 @@ import ProgressBar from "../ProgressBar";
|
|||||||
import SystemItem from "../SystemItem";
|
import SystemItem from "../SystemItem";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { appDownloadProgress } from "@/_helpers/JotaiWrapper";
|
import { appDownloadProgress } from "@/_helpers/JotaiWrapper";
|
||||||
import { currentProductAtom } from "@/_helpers/atoms/Model.atom";
|
|
||||||
import useGetAppVersion from "@/_hooks/useGetAppVersion";
|
import useGetAppVersion from "@/_hooks/useGetAppVersion";
|
||||||
import useGetSystemResources from "@/_hooks/useGetSystemResources";
|
import useGetSystemResources from "@/_hooks/useGetSystemResources";
|
||||||
import { modelDownloadStateAtom } from "@/_helpers/atoms/DownloadState.atom";
|
import { modelDownloadStateAtom } from "@/_helpers/atoms/DownloadState.atom";
|
||||||
import { DownloadState } from "@/_models/DownloadState";
|
import { DownloadState } from "@/_models/DownloadState";
|
||||||
import { formatDownloadPercentage } from "@/_utils/converter";
|
import { formatDownloadPercentage } from "@/_utils/converter";
|
||||||
|
import { activeAssistantModelAtom } from "@/_helpers/atoms/Model.atom";
|
||||||
|
|
||||||
const MonitorBar: React.FC = () => {
|
const MonitorBar: React.FC = () => {
|
||||||
const progress = useAtomValue(appDownloadProgress);
|
const progress = useAtomValue(appDownloadProgress);
|
||||||
const activeModel = useAtomValue(currentProductAtom);
|
const activeModel = useAtomValue(activeAssistantModelAtom);
|
||||||
const { version } = useGetAppVersion();
|
const { version } = useGetAppVersion();
|
||||||
const { ram, cpu } = useGetSystemResources();
|
const { ram, cpu } = useGetSystemResources();
|
||||||
const modelDownloadStates = useAtomValue(modelDownloadStateAtom);
|
const modelDownloadStates = useAtomValue(modelDownloadStateAtom);
|
||||||
|
|||||||
@ -7,14 +7,14 @@ import {
|
|||||||
MainViewState,
|
MainViewState,
|
||||||
setMainViewStateAtom,
|
setMainViewStateAtom,
|
||||||
} from "@/_helpers/atoms/MainView.atom";
|
} from "@/_helpers/atoms/MainView.atom";
|
||||||
import { currentProductAtom } from "@/_helpers/atoms/Model.atom";
|
|
||||||
import useCreateConversation from "@/_hooks/useCreateConversation";
|
import useCreateConversation from "@/_hooks/useCreateConversation";
|
||||||
import useInitModel from "@/_hooks/useInitModel";
|
import useInitModel from "@/_hooks/useInitModel";
|
||||||
import { Product } from "@/_models/Product";
|
|
||||||
import { PlusIcon } from "@heroicons/react/24/outline";
|
import { PlusIcon } from "@heroicons/react/24/outline";
|
||||||
|
import { activeAssistantModelAtom } from "@/_helpers/atoms/Model.atom";
|
||||||
|
import { AssistantModel } from "@/_models/AssistantModel";
|
||||||
|
|
||||||
const NewChatButton: React.FC = () => {
|
const NewChatButton: React.FC = () => {
|
||||||
const activeModel = useAtomValue(currentProductAtom);
|
const activeModel = useAtomValue(activeAssistantModelAtom);
|
||||||
const setMainView = useSetAtom(setMainViewStateAtom);
|
const setMainView = useSetAtom(setMainViewStateAtom);
|
||||||
const { requestCreateConvo } = useCreateConversation();
|
const { requestCreateConvo } = useCreateConversation();
|
||||||
const { initModel } = useInitModel();
|
const { initModel } = useInitModel();
|
||||||
@ -27,7 +27,7 @@ const NewChatButton: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const createConversationAndInitModel = async (model: Product) => {
|
const createConversationAndInitModel = async (model: AssistantModel) => {
|
||||||
await requestCreateConvo(model);
|
await requestCreateConvo(model);
|
||||||
await initModel(model);
|
await initModel(model);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,10 +7,10 @@ import {
|
|||||||
MainViewState,
|
MainViewState,
|
||||||
setMainViewStateAtom,
|
setMainViewStateAtom,
|
||||||
} from "@/_helpers/atoms/MainView.atom";
|
} from "@/_helpers/atoms/MainView.atom";
|
||||||
import { currentProductAtom } from "@/_helpers/atoms/Model.atom";
|
import { activeAssistantModelAtom } from "@/_helpers/atoms/Model.atom";
|
||||||
import useInitModel from "@/_hooks/useInitModel";
|
import useInitModel from "@/_hooks/useInitModel";
|
||||||
import { Product } from "@/_models/Product";
|
|
||||||
import { useGetDownloadedModels } from "@/_hooks/useGetDownloadedModels";
|
import { useGetDownloadedModels } from "@/_hooks/useGetDownloadedModels";
|
||||||
|
import { AssistantModel } from "@/_models/AssistantModel";
|
||||||
|
|
||||||
enum ActionButton {
|
enum ActionButton {
|
||||||
DownloadModel = "Download a Model",
|
DownloadModel = "Download a Model",
|
||||||
@ -19,7 +19,7 @@ enum ActionButton {
|
|||||||
|
|
||||||
const SidebarEmptyHistory: React.FC = () => {
|
const SidebarEmptyHistory: React.FC = () => {
|
||||||
const { downloadedModels } = useGetDownloadedModels();
|
const { downloadedModels } = useGetDownloadedModels();
|
||||||
const activeModel = useAtomValue(currentProductAtom);
|
const activeModel = useAtomValue(activeAssistantModelAtom);
|
||||||
const setMainView = useSetAtom(setMainViewStateAtom);
|
const setMainView = useSetAtom(setMainViewStateAtom);
|
||||||
const { requestCreateConvo } = useCreateConversation();
|
const { requestCreateConvo } = useCreateConversation();
|
||||||
const [action, setAction] = useState(ActionButton.DownloadModel);
|
const [action, setAction] = useState(ActionButton.DownloadModel);
|
||||||
@ -46,7 +46,7 @@ const SidebarEmptyHistory: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const createConversationAndInitModel = async (model: Product) => {
|
const createConversationAndInitModel = async (model: AssistantModel) => {
|
||||||
await requestCreateConvo(model);
|
await requestCreateConvo(model);
|
||||||
await initModel(model);
|
await initModel(model);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
import { Product } from "@/_models/Product";
|
import { AssistantModel } from "@/_models/AssistantModel";
|
||||||
import { atom } from "jotai";
|
import { atom } from "jotai";
|
||||||
|
|
||||||
export const downloadedModelAtom = atom<Product[]>([]);
|
/**
|
||||||
|
* @description: This atom is used to store the downloaded models
|
||||||
|
*/
|
||||||
|
export const downloadedModelAtom = atom<AssistantModel[]>([]);
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import { Product } from "@/_models/Product";
|
import { AssistantModel } from "@/_models/AssistantModel";
|
||||||
import { atom } from "jotai";
|
import { atom } from "jotai";
|
||||||
|
|
||||||
export const currentProductAtom = atom<Product | undefined>(undefined);
|
export const selectedModelAtom = atom<AssistantModel | undefined>(undefined);
|
||||||
|
|
||||||
export const selectedModelAtom = atom<Product | undefined>(undefined);
|
export const activeAssistantModelAtom = atom<AssistantModel | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { useAtom, useSetAtom } from "jotai";
|
|||||||
import { Conversation } from "@/_models/Conversation";
|
import { Conversation } from "@/_models/Conversation";
|
||||||
import { executeSerial } from "@/_services/pluginService";
|
import { executeSerial } from "@/_services/pluginService";
|
||||||
import { DataService } from "../../shared/coreService";
|
import { DataService } from "../../shared/coreService";
|
||||||
import { Product } from "@/_models/Product";
|
|
||||||
import {
|
import {
|
||||||
userConversationsAtom,
|
userConversationsAtom,
|
||||||
setActiveConvoIdAtom,
|
setActiveConvoIdAtom,
|
||||||
@ -11,6 +10,7 @@ import {
|
|||||||
updateConversationErrorAtom,
|
updateConversationErrorAtom,
|
||||||
} from "@/_helpers/atoms/Conversation.atom";
|
} from "@/_helpers/atoms/Conversation.atom";
|
||||||
import useInitModel from "./useInitModel";
|
import useInitModel from "./useInitModel";
|
||||||
|
import { AssistantModel } from "@/_models/AssistantModel";
|
||||||
|
|
||||||
const useCreateConversation = () => {
|
const useCreateConversation = () => {
|
||||||
const { initModel } = useInitModel();
|
const { initModel } = useInitModel();
|
||||||
@ -24,7 +24,7 @@ const useCreateConversation = () => {
|
|||||||
);
|
);
|
||||||
const updateConvError = useSetAtom(updateConversationErrorAtom);
|
const updateConvError = useSetAtom(updateConversationErrorAtom);
|
||||||
|
|
||||||
const requestCreateConvo = async (model: Product) => {
|
const requestCreateConvo = async (model: AssistantModel) => {
|
||||||
const conversationName = model.name;
|
const conversationName = model.name;
|
||||||
const conv: Conversation = {
|
const conv: Conversation = {
|
||||||
model_id: model.id,
|
model_id: model.id,
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import { execute, executeSerial } from "@/_services/pluginService";
|
import { execute, executeSerial } from "@/_services/pluginService";
|
||||||
import { DataService, ModelManagementService } from "../../shared/coreService";
|
import { DataService, ModelManagementService } from "../../shared/coreService";
|
||||||
import { Product } from "@/_models/Product";
|
|
||||||
import { useSetAtom } from "jotai";
|
import { useSetAtom } from "jotai";
|
||||||
import { downloadedModelAtom } from "@/_helpers/atoms/DownloadedModel.atom";
|
import { downloadedModelAtom } from "@/_helpers/atoms/DownloadedModel.atom";
|
||||||
import { getDownloadedModels } from "./useGetDownloadedModels";
|
import { getDownloadedModels } from "./useGetDownloadedModels";
|
||||||
|
import { AssistantModel } from "@/_models/AssistantModel";
|
||||||
|
|
||||||
export default function useDeleteModel() {
|
export default function useDeleteModel() {
|
||||||
const setDownloadedModels = useSetAtom(downloadedModelAtom);
|
const setDownloadedModels = useSetAtom(downloadedModelAtom);
|
||||||
|
|
||||||
const deleteModel = async (model: Product) => {
|
const deleteModel = async (model: AssistantModel) => {
|
||||||
execute(DataService.DELETE_DOWNLOAD_MODEL, model.id);
|
execute(DataService.DELETE_DOWNLOAD_MODEL, model.id);
|
||||||
await executeSerial(ModelManagementService.DELETE_MODEL, model.fileName);
|
await executeSerial(ModelManagementService.DELETE_MODEL, model.id);
|
||||||
|
|
||||||
// reload models
|
// reload models
|
||||||
const downloadedModels = await getDownloadedModels();
|
const downloadedModels = await getDownloadedModels();
|
||||||
|
|||||||
@ -1,38 +1,20 @@
|
|||||||
import { executeSerial } from "@/_services/pluginService";
|
import { executeSerial } from "@/_services/pluginService";
|
||||||
import { DataService, ModelManagementService } from "../../shared/coreService";
|
import { DataService, ModelManagementService } from "../../shared/coreService";
|
||||||
import { ModelVersion, Product } from "@/_models/Product";
|
import { Product } from "@/_models/Product";
|
||||||
|
import { ModelVersion } from "@/_models/ModelVersion";
|
||||||
|
|
||||||
export default function useDownloadModel() {
|
export default function useDownloadModel() {
|
||||||
const downloadModel = async (model: Product) => {
|
const downloadModel = async (model: Product, modelVersion: ModelVersion) => {
|
||||||
await executeSerial(DataService.STORE_MODEL, model);
|
modelVersion.startDownloadAt = Date.now();
|
||||||
await executeSerial(ModelManagementService.DOWNLOAD_MODEL, {
|
|
||||||
downloadUrl: model.downloadUrl,
|
|
||||||
fileName: model.fileName,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const downloadHfModel = async (
|
await executeSerial(DataService.STORE_MODEL, { model, modelVersion });
|
||||||
model: Product,
|
|
||||||
modelVersion: ModelVersion
|
|
||||||
) => {
|
|
||||||
const hfModel: Product = {
|
|
||||||
...model,
|
|
||||||
id: `${model.author}.${modelVersion.path}`,
|
|
||||||
slug: `${model.author}.${modelVersion.path}`,
|
|
||||||
name: `${model.name} - ${modelVersion.path}`,
|
|
||||||
fileName: modelVersion.path,
|
|
||||||
totalSize: modelVersion.size,
|
|
||||||
downloadUrl: modelVersion.downloadUrl,
|
|
||||||
};
|
|
||||||
await executeSerial(DataService.STORE_MODEL, hfModel);
|
|
||||||
await executeSerial(ModelManagementService.DOWNLOAD_MODEL, {
|
await executeSerial(ModelManagementService.DOWNLOAD_MODEL, {
|
||||||
downloadUrl: hfModel.downloadUrl,
|
downloadUrl: modelVersion.downloadLink,
|
||||||
fileName: hfModel.fileName,
|
fileName: modelVersion.id,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
downloadModel,
|
downloadModel,
|
||||||
downloadHfModel,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,54 +0,0 @@
|
|||||||
import { Product } from "@/_models/Product";
|
|
||||||
import { executeSerial } from "@/_services/pluginService";
|
|
||||||
import { ModelManagementService } from "../../shared/coreService";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { getModelFiles } from "./useGetDownloadedModels";
|
|
||||||
import { useAtomValue } from "jotai";
|
|
||||||
import { modelDownloadStateAtom } from "@/_helpers/atoms/DownloadState.atom";
|
|
||||||
|
|
||||||
export default function useGetAvailableModels() {
|
|
||||||
const downloadState = useAtomValue(modelDownloadStateAtom);
|
|
||||||
const [allAvailableModels, setAllAvailableModels] = useState<Product[]>([]);
|
|
||||||
const [availableModels, setAvailableModels] = useState<Product[]>([]);
|
|
||||||
const [downloadedModels, setDownloadedModels] = useState<Product[]>([]);
|
|
||||||
|
|
||||||
const getAvailableModelExceptDownloaded = async () => {
|
|
||||||
const avails = await getAvailableModels();
|
|
||||||
const downloaded = await getModelFiles();
|
|
||||||
|
|
||||||
setAllAvailableModels(avails);
|
|
||||||
const availableOrDownloadingModels: Product[] = avails;
|
|
||||||
const successfullDownloadModels: Product[] = [];
|
|
||||||
|
|
||||||
downloaded.forEach((item) => {
|
|
||||||
if (item.fileName && downloadState[item.fileName] == null) {
|
|
||||||
// if not downloading, consider as downloaded
|
|
||||||
successfullDownloadModels.push(item);
|
|
||||||
} else {
|
|
||||||
availableOrDownloadingModels.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setAvailableModels(availableOrDownloadingModels);
|
|
||||||
setDownloadedModels(successfullDownloadModels);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getAvailableModelExceptDownloaded();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return {
|
|
||||||
allAvailableModels,
|
|
||||||
availableModels,
|
|
||||||
downloadedModels,
|
|
||||||
getAvailableModelExceptDownloaded,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getAvailableModels(): Promise<Product[]> {
|
|
||||||
const avails: Product[] = await executeSerial(
|
|
||||||
ModelManagementService.GET_AVAILABLE_MODELS
|
|
||||||
);
|
|
||||||
|
|
||||||
return avails ?? [];
|
|
||||||
}
|
|
||||||
20
web/app/_hooks/useGetConfiguredModels.ts
Normal file
20
web/app/_hooks/useGetConfiguredModels.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { Product } from "@/_models/Product";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { getConfiguredModels } from "./useGetDownloadedModels";
|
||||||
|
|
||||||
|
export default function useGetConfiguredModels() {
|
||||||
|
const [models, setModels] = useState<Product[]>([]);
|
||||||
|
|
||||||
|
const fetchModels = async () => {
|
||||||
|
const models = await getConfiguredModels();
|
||||||
|
|
||||||
|
setModels(models);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO allow user for filter
|
||||||
|
useEffect(() => {
|
||||||
|
fetchModels();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { models };
|
||||||
|
}
|
||||||
@ -1,10 +1,10 @@
|
|||||||
import { ModelVersion, Product, ProductType } from "@/_models/Product";
|
import { Product } from "@/_models/Product";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { executeSerial } from "../../../electron/core/plugin-manager/execution/extension-manager";
|
import { executeSerial } from "../../../electron/core/plugin-manager/execution/extension-manager";
|
||||||
import { DataService, ModelManagementService } from "../../shared/coreService";
|
import { DataService, ModelManagementService } from "../../shared/coreService";
|
||||||
import { SearchModelParamHf } from "@/_models/hf/SearchModelParam.hf";
|
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { downloadedModelAtom } from "@/_helpers/atoms/DownloadedModel.atom";
|
import { downloadedModelAtom } from "@/_helpers/atoms/DownloadedModel.atom";
|
||||||
|
import { AssistantModel } from "@/_models/AssistantModel";
|
||||||
|
|
||||||
export function useGetDownloadedModels() {
|
export function useGetDownloadedModels() {
|
||||||
const [downloadedModels, setDownloadedModels] = useAtom(downloadedModelAtom);
|
const [downloadedModels, setDownloadedModels] = useAtom(downloadedModelAtom);
|
||||||
@ -18,76 +18,13 @@ export function useGetDownloadedModels() {
|
|||||||
return { downloadedModels };
|
return { downloadedModels };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getDownloadedModels(): Promise<Product[]> {
|
export async function getDownloadedModels(): Promise<AssistantModel[]> {
|
||||||
const downloadedModels: Product[] = await executeSerial(
|
const downloadedModels: AssistantModel[] = await executeSerial(
|
||||||
DataService.GET_FINISHED_DOWNLOAD_MODELS
|
DataService.GET_FINISHED_DOWNLOAD_MODELS
|
||||||
);
|
);
|
||||||
return downloadedModels ?? [];
|
return downloadedModels ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getModelFiles(): Promise<Product[]> {
|
export async function getConfiguredModels(): Promise<Product[]> {
|
||||||
const downloadedModels: Product[] = await executeSerial(
|
return executeSerial(ModelManagementService.GET_CONFIGURED_MODELS);
|
||||||
ModelManagementService.GET_DOWNLOADED_MODELS
|
|
||||||
);
|
|
||||||
return downloadedModels ?? [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function searchModels(
|
|
||||||
params: SearchModelParamHf
|
|
||||||
): Promise<QueryProductResult> {
|
|
||||||
const result = await executeSerial(
|
|
||||||
ModelManagementService.SEARCH_MODELS,
|
|
||||||
params
|
|
||||||
);
|
|
||||||
|
|
||||||
const products: Product[] = result.data.map((model: any) => {
|
|
||||||
const modelVersions: ModelVersion[] = [];
|
|
||||||
|
|
||||||
for (const [, file] of Object.entries(model.files)) {
|
|
||||||
const fileData: any = file as any;
|
|
||||||
const modelVersion: ModelVersion = {
|
|
||||||
path: fileData.path,
|
|
||||||
type: fileData.type,
|
|
||||||
downloadUrl: fileData.downloadLink,
|
|
||||||
size: fileData.size,
|
|
||||||
};
|
|
||||||
modelVersions.push(modelVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
const p = {
|
|
||||||
id: model.id,
|
|
||||||
slug: model.name,
|
|
||||||
name: model.name,
|
|
||||||
description: model.name,
|
|
||||||
avatarUrl: "",
|
|
||||||
longDescription: model.name,
|
|
||||||
technicalDescription: model.name,
|
|
||||||
author: model.name.split("/")[0],
|
|
||||||
version: "1.0.0",
|
|
||||||
modelUrl: "https://google.com",
|
|
||||||
nsfw: false,
|
|
||||||
greeting: "Hello there",
|
|
||||||
type: ProductType.LLM,
|
|
||||||
createdAt: -1,
|
|
||||||
accelerated: true,
|
|
||||||
totalSize: -1,
|
|
||||||
format: "",
|
|
||||||
status: "Not downloaded",
|
|
||||||
releaseDate: -1,
|
|
||||||
availableVersions: modelVersions,
|
|
||||||
};
|
|
||||||
|
|
||||||
return p;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
data: products,
|
|
||||||
hasMore: result.hasMore,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO define somewhere else
|
|
||||||
export type QueryProductResult = {
|
|
||||||
data: Product[];
|
|
||||||
hasMore: boolean;
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,18 +1,19 @@
|
|||||||
import { Product } from "@/_models/Product";
|
|
||||||
import { executeSerial } from "@/_services/pluginService";
|
import { executeSerial } from "@/_services/pluginService";
|
||||||
import { InferenceService } from "../../shared/coreService";
|
import { InferenceService } from "../../shared/coreService";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { currentProductAtom } from "@/_helpers/atoms/Model.atom";
|
import { activeAssistantModelAtom } from "@/_helpers/atoms/Model.atom";
|
||||||
|
import { AssistantModel } from "@/_models/AssistantModel";
|
||||||
|
|
||||||
export default function useInitModel() {
|
export default function useInitModel() {
|
||||||
const [activeModel, setActiveModel] = useAtom(currentProductAtom);
|
const [activeModel, setActiveModel] = useAtom(activeAssistantModelAtom);
|
||||||
|
|
||||||
const initModel = async (model: Product) => {
|
const initModel = async (model: AssistantModel) => {
|
||||||
if (activeModel && activeModel.id === model.id) {
|
if (activeModel && activeModel.id === model.id) {
|
||||||
console.debug(`Model ${model.id} is already init. Ignore..`);
|
console.debug(`Model ${model.id} is already init. Ignore..`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const res = await executeSerial(InferenceService.INIT_MODEL, model);
|
|
||||||
|
const res = await executeSerial(InferenceService.INIT_MODEL, model.id);
|
||||||
if (res?.error) {
|
if (res?.error) {
|
||||||
console.log("error occured: ", res);
|
console.log("error occured: ", res);
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
@ -2,11 +2,11 @@ import { executeSerial } from "@/_services/pluginService";
|
|||||||
import { DataService, InferenceService } from "../../shared/coreService";
|
import { DataService, InferenceService } from "../../shared/coreService";
|
||||||
import useInitModel from "./useInitModel";
|
import useInitModel from "./useInitModel";
|
||||||
import { useSetAtom } from "jotai";
|
import { useSetAtom } from "jotai";
|
||||||
import { currentProductAtom } from "@/_helpers/atoms/Model.atom";
|
import { activeAssistantModelAtom } from "@/_helpers/atoms/Model.atom";
|
||||||
|
|
||||||
export default function useStartStopModel() {
|
export default function useStartStopModel() {
|
||||||
const { initModel } = useInitModel();
|
const { initModel } = useInitModel();
|
||||||
const setActiveModel = useSetAtom(currentProductAtom);
|
const setActiveModel = useSetAtom(activeAssistantModelAtom);
|
||||||
|
|
||||||
const startModel = async (modelId: string) => {
|
const startModel = async (modelId: string) => {
|
||||||
const model = await executeSerial(DataService.GET_MODEL_BY_ID, modelId);
|
const model = await executeSerial(DataService.GET_MODEL_BY_ID, modelId);
|
||||||
|
|||||||
65
web/app/_models/AssistantModel.ts
Normal file
65
web/app/_models/AssistantModel.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { ProductType } from "./Product";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent a model
|
||||||
|
*/
|
||||||
|
export type AssistantModel = {
|
||||||
|
/**
|
||||||
|
* Combination of owner and model name.
|
||||||
|
* Being used as file name. MUST be unique.
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
quantMethod: string;
|
||||||
|
|
||||||
|
bits: number;
|
||||||
|
|
||||||
|
size: number;
|
||||||
|
|
||||||
|
maxRamRequired: number;
|
||||||
|
|
||||||
|
usecase: string;
|
||||||
|
|
||||||
|
downloadLink: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For tracking download info
|
||||||
|
*/
|
||||||
|
startDownloadAt?: number;
|
||||||
|
|
||||||
|
finishDownloadAt?: number;
|
||||||
|
|
||||||
|
productId: string;
|
||||||
|
|
||||||
|
productName: string;
|
||||||
|
|
||||||
|
shortDescription: string;
|
||||||
|
|
||||||
|
longDescription: string;
|
||||||
|
|
||||||
|
avatarUrl: string;
|
||||||
|
|
||||||
|
author: string;
|
||||||
|
|
||||||
|
version: string;
|
||||||
|
|
||||||
|
modelUrl: string;
|
||||||
|
|
||||||
|
nsfw: boolean;
|
||||||
|
|
||||||
|
greeting: string;
|
||||||
|
|
||||||
|
type: ProductType;
|
||||||
|
|
||||||
|
createdAt: number;
|
||||||
|
|
||||||
|
updatedAt?: number;
|
||||||
|
|
||||||
|
status: string; // TODO: add this in the database // Downloaded, Active
|
||||||
|
|
||||||
|
releaseDate: number; // TODO: add this in the database
|
||||||
|
|
||||||
|
tags: string[];
|
||||||
|
};
|
||||||
23
web/app/_models/ModelVersion.ts
Normal file
23
web/app/_models/ModelVersion.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Model type which will be stored in the database
|
||||||
|
*/
|
||||||
|
export type ModelVersion = {
|
||||||
|
/**
|
||||||
|
* Combination of owner and model name.
|
||||||
|
* Being used as file name. Should be unique.
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
quantMethod: string;
|
||||||
|
bits: number;
|
||||||
|
size: number;
|
||||||
|
maxRamRequired: number;
|
||||||
|
usecase: string;
|
||||||
|
downloadLink: string;
|
||||||
|
productId: string;
|
||||||
|
/**
|
||||||
|
* For tracking download state
|
||||||
|
*/
|
||||||
|
startDownloadAt?: number;
|
||||||
|
finishDownloadAt?: number;
|
||||||
|
};
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ModelVersion } from "./ModelVersion";
|
||||||
import { ProductInput } from "./ProductInput";
|
import { ProductInput } from "./ProductInput";
|
||||||
import { ProductOutput } from "./ProductOutput";
|
import { ProductOutput } from "./ProductOutput";
|
||||||
|
|
||||||
@ -9,12 +10,10 @@ export enum ProductType {
|
|||||||
|
|
||||||
export interface Product {
|
export interface Product {
|
||||||
id: string;
|
id: string;
|
||||||
slug: string;
|
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
shortDescription: string;
|
||||||
avatarUrl: string;
|
avatarUrl: string;
|
||||||
longDescription: string;
|
longDescription: string;
|
||||||
technicalDescription: string;
|
|
||||||
author: string;
|
author: string;
|
||||||
version: string;
|
version: string;
|
||||||
modelUrl: string;
|
modelUrl: string;
|
||||||
@ -25,36 +24,8 @@ export interface Product {
|
|||||||
outputs?: ProductOutput;
|
outputs?: ProductOutput;
|
||||||
createdAt: number;
|
createdAt: number;
|
||||||
updatedAt?: number;
|
updatedAt?: number;
|
||||||
fileName?: string;
|
|
||||||
downloadUrl?: string;
|
|
||||||
|
|
||||||
accelerated: boolean; // TODO: add this in the database
|
|
||||||
totalSize: number; // TODO: add this in the database
|
|
||||||
format: string; // TODO: add this in the database // GGUF or something else
|
|
||||||
status: string; // TODO: add this in the database // Downloaded, Active
|
status: string; // TODO: add this in the database // Downloaded, Active
|
||||||
releaseDate: number; // TODO: add this in the database
|
releaseDate: number; // TODO: add this in the database
|
||||||
|
tags: string[];
|
||||||
availableVersions: ModelVersion[];
|
availableVersions: ModelVersion[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ModelVersion {
|
|
||||||
/**
|
|
||||||
* Act as the id of the model version
|
|
||||||
*/
|
|
||||||
path: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* currently, we only have `file` type
|
|
||||||
*/
|
|
||||||
type: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The download url for the model version
|
|
||||||
*/
|
|
||||||
downloadUrl: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* File size in bytes
|
|
||||||
*/
|
|
||||||
size: number;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ export const isCorePluginInstalled = () => {
|
|||||||
if (!extensionPoints.get(InferenceService.INIT_MODEL)) {
|
if (!extensionPoints.get(InferenceService.INIT_MODEL)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!extensionPoints.get(ModelManagementService.GET_DOWNLOADED_MODELS)) {
|
if (!extensionPoints.get(ModelManagementService.DOWNLOAD_MODEL)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -34,7 +34,7 @@ export const setupBasePlugins = async () => {
|
|||||||
if (
|
if (
|
||||||
!extensionPoints.get(DataService.GET_CONVERSATIONS) ||
|
!extensionPoints.get(DataService.GET_CONVERSATIONS) ||
|
||||||
!extensionPoints.get(InferenceService.INIT_MODEL) ||
|
!extensionPoints.get(InferenceService.INIT_MODEL) ||
|
||||||
!extensionPoints.get(ModelManagementService.GET_DOWNLOADED_MODELS)
|
!extensionPoints.get(ModelManagementService.DOWNLOAD_MODEL)
|
||||||
) {
|
) {
|
||||||
const installed = await plugins.install(basePlugins);
|
const installed = await plugins.install(basePlugins);
|
||||||
if (installed) {
|
if (installed) {
|
||||||
|
|||||||
@ -34,11 +34,10 @@ export enum InferenceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum ModelManagementService {
|
export enum ModelManagementService {
|
||||||
GET_DOWNLOADED_MODELS = "getDownloadedModels",
|
|
||||||
GET_AVAILABLE_MODELS = "getAvailableModels",
|
|
||||||
DELETE_MODEL = "deleteModel",
|
DELETE_MODEL = "deleteModel",
|
||||||
DOWNLOAD_MODEL = "downloadModel",
|
DOWNLOAD_MODEL = "downloadModel",
|
||||||
SEARCH_MODELS = "searchModels",
|
SEARCH_MODELS = "searchModels",
|
||||||
|
GET_CONFIGURED_MODELS = "getConfiguredModels",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum PreferenceService {
|
export enum PreferenceService {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user