Louis afbb94f083
efactor app directories and enforce ts strict mode (#201)
* refactor: move Electron app to main directory and enforce ts strict mode

* chore: add pre-install plugins

* remove duplicated initModel function

Signed-off-by: James <james@jan.ai>

* chore: correct module path

* fix: dynamic import does not work with ts

* chore: web should be able to run on target host browser

* fix: history panel, should display conversations rather just blank state

* chore: init default model

* chore: pluggin in ts

* fix: pre-pack model management

* fix: compiled core should not include plugins

* chore: refactor - invoke plugin function

* refactor download/delete file

Signed-off-by: James <james@jan.ai>

* update prebuild lib

Signed-off-by: James <james@jan.ai>

* chore: yarn workspace

* chore: update yarn workspace

* chore: yarn workspace with nohoist

* fix: llama-cpp-import

* chore: fix data-plugin wrong module path

* chore: correct build step

* chore: 	- separate inference service (#212)

- remove base-plugin

Signed-off-by: James <james@jan.ai>
Co-authored-by: James <james@jan.ai>

* chore: update core plugins

* chore: hide installation prompt and fix model load - management plugin

* chore: remove legacy files; update readme

* fix: refresh page lost the download state

Signed-off-by: James <james@jan.ai>

* fix: ai prompt not passed to plugin

Signed-off-by: James <james@jan.ai>

* chore: module import fix for production

* chore: auto updater

* chore: package is public

* chore: fix yarn workspace config

* update: model management uses Q4_K_M

* chore: fix yarn scripts for publishing

* chore: app updater - progress update message

* chore: user confirms update action

* adding some state for changing page
store downloaded model to database

Signed-off-by: James <james@jan.ai>

* chore: refactor plugins into yarn workspace - a single command to publish all base plugins

* chore update readme (#218)

Co-authored-by: Hien To <tominhhien97@gmail.com>

* change app name and app icon

Signed-off-by: James <james@jan.ai>

* remove: go-to-nowhere actions

* chore: bundle core plugins from root and scan default plugins

* fix: app crashes on different field name lookup

* chore: css fix

* chore: bind download progress to app ui

* chore: bind active model

* chore: simplify app splash-screen only centered jan icon

* feature: system monitoring plugin (#196)

* feat: Add function for system monitoring

* chore: register plugin functions

* chore: move to corresponding directory

* chore: bind system monitoring data to UI

---------

Co-authored-by: Louis <louis@jan.ai>

* chore: add build:plugins step to README

* chore: model searching and fix model name

* fix: plugin file selected appearance

* fix: create new conversation does not work

* fix: delete conversation not update state - messages still exist

* chore: fix asset path prefix

* Add CICD for macos (#221)

Co-authored-by: Hien To <tominhhien97@gmail.com>

* chore: fix production plugin path

* chore: add shell open url in external browser

---------

Signed-off-by: James <james@jan.ai>
Co-authored-by: James <james@jan.ai>
Co-authored-by: NamH <NamNh0122@gmail.com>
Co-authored-by: 0xSage <n@pragmatic.vc>
Co-authored-by: hiento09 <136591877+hiento09@users.noreply.github.com>
Co-authored-by: Hien To <tominhhien97@gmail.com>
Co-authored-by: namvuong <22463238+vuonghoainam@users.noreply.github.com>
2023-09-28 18:15:18 +07:00

372 lines
9.1 KiB
TypeScript

const sqlite3 = require("sqlite3").verbose();
const path = require("path");
const { app } = require("electron");
const MODEL_TABLE_CREATION = `
CREATE TABLE IF NOT EXISTS models (
id TEXT PRIMARY KEY,
slug TEXT NOT NULL,
name TEXT NOT NULL,
description TEXT NOT NULL,
avatar_url TEXT,
long_description TEXT NOT NULL,
technical_description TEXT NOT NULL,
author TEXT NOT NULL,
version TEXT NOT NULL,
model_url TEXT NOT NULL,
nsfw INTEGER NOT NULL,
greeting 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,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);`;
const MODEL_TABLE_INSERTION = `
INSERT INTO models (
id,
slug,
name,
description,
avatar_url,
long_description,
technical_description,
author,
version,
model_url,
nsfw,
greeting,
type,
file_name,
download_url,
start_download_at
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`;
function init() {
const db = new sqlite3.Database(path.join(app.getPath("userData"), "jan.db"));
console.log(
`Database located at ${path.join(app.getPath("userData"), "jan.db")}`
);
db.serialize(() => {
db.run(MODEL_TABLE_CREATION);
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);"
);
db.run(
"CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY, name TEXT, conversation_id INTEGER, user TEXT, message TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP);"
);
});
const stmt = db.prepare(
"INSERT INTO conversations (name, model_id, image, message) VALUES (?, ?, ?, ?)"
);
stmt.finalize();
db.close();
}
/**
* Store a model in the database when user start downloading it
*
* @param model Product
*/
function storeModel(model: any) {
return new Promise((res) => {
const db = new sqlite3.Database(
path.join(app.getPath("userData"), "jan.db")
);
console.debug("Inserting", JSON.stringify(model));
db.serialize(() => {
const stmt = db.prepare(MODEL_TABLE_INSERTION);
stmt.run(
model.id,
model.slug,
model.name,
model.description,
model.avatarUrl,
model.longDescription,
model.technicalDescription,
model.author,
model.version,
model.modelUrl,
model.nsfw,
model.greeting,
model.type,
model.fileName,
model.downloadUrl,
Date.now(),
function (err: any) {
if (err) {
// Handle the insertion error here
console.error(err.message);
res(undefined);
return;
}
// @ts-ignoreF
const id = this.lastID;
res(id);
return;
}
);
stmt.finalize();
});
db.close();
});
}
/**
* Update the finished download time of a model
*
* @param model Product
*/
function updateFinishedDownloadAt(fileName: string, time: number) {
return new Promise((res) => {
const db = new sqlite3.Database(
path.join(app.getPath("userData"), "jan.db")
);
console.debug(`Updating fileName ${fileName} to ${time}`);
const stmt = `UPDATE models SET finish_download_at = ? WHERE file_name = ?`;
db.run(stmt, [time, fileName], (err: any) => {
if (err) {
console.log(err);
} else {
console.log("Updated 1 row");
res("Updated");
}
});
db.close();
});
}
/**
* Get all unfinished models from the database
*/
function getUnfinishedDownloadModels() {
return new Promise((res) => {
const db = new sqlite3.Database(
path.join(app.getPath("userData"), "jan.db")
);
const query = `SELECT * FROM models WHERE finish_download_at = -1 ORDER BY start_download_at DESC`;
db.all(query, (err: Error, row: any) => {
res(row);
});
db.close();
});
}
function getFinishedDownloadModels() {
return new Promise((res) => {
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`;
db.all(query, (err: Error, row: any) => {
res(row);
});
db.close();
});
}
function deleteDownloadModel(modelId: string) {
return new Promise((res) => {
const db = new sqlite3.Database(
path.join(app.getPath("userData"), "jan.db")
);
console.log(`Deleting ${modelId}`);
db.serialize(() => {
const stmt = db.prepare("DELETE FROM models WHERE id = ?");
stmt.run(modelId);
stmt.finalize();
});
db.close();
});
}
function getModelById(modelId: string) {
return new Promise((res) => {
const db = new sqlite3.Database(
path.join(app.getPath("userData"), "jan.db")
);
console.debug("Get model by id", modelId);
db.get(
`SELECT * FROM models WHERE id = ?`,
[modelId],
(err: any, row: any) => {
console.debug("Get model by id result", row);
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,
};
res(product);
}
}
);
db.close();
});
}
function getConversations() {
return new Promise((res) => {
const db = new sqlite3.Database(
path.join(app.getPath("userData"), "jan.db")
);
db.all(
"SELECT * FROM conversations ORDER BY created_at DESC",
(err: any, row: any) => {
res(row);
}
);
db.close();
});
}
function storeConversation(conversation: any) {
return new Promise((res) => {
const db = new sqlite3.Database(
path.join(app.getPath("userData"), "jan.db")
);
db.serialize(() => {
const stmt = db.prepare(
"INSERT INTO conversations (name, model_id, image, message) VALUES (?, ?, ?, ?)"
);
stmt.run(
conversation.name,
conversation.model_id,
conversation.image,
conversation.message,
function (err: any) {
if (err) {
// Handle the insertion error here
console.error(err.message);
res(undefined);
return;
}
// @ts-ignoreF
const id = this.lastID;
res(id);
return;
}
);
stmt.finalize();
});
db.close();
});
}
function storeMessage(message: any) {
return new Promise((res) => {
const db = new sqlite3.Database(
path.join(app.getPath("userData"), "jan.db")
);
db.serialize(() => {
const stmt = db.prepare(
"INSERT INTO messages (name, conversation_id, user, message) VALUES (?, ?, ?, ?)"
);
stmt.run(
message.name,
message.conversation_id,
message.user,
message.message,
(err: any) => {
if (err) {
// Handle the insertion error here
console.error(err.message);
res(undefined);
return;
}
//@ts-ignore
const id = this.lastID;
res(id);
return;
}
);
stmt.finalize();
});
db.close();
});
}
function deleteConversation(id: any) {
return new Promise((res) => {
const db = new sqlite3.Database(
path.join(app.getPath("userData"), "jan.db")
);
db.serialize(() => {
const deleteConv = db.prepare("DELETE FROM conversations WHERE id = ?");
deleteConv.run(id);
deleteConv.finalize();
const deleteMessages = db.prepare(
"DELETE FROM messages WHERE conversation_id = ?"
);
deleteMessages.run(id);
deleteMessages.finalize();
res([]);
});
db.close();
});
}
function getConversationMessages(conversation_id: any) {
return new Promise((res) => {
const db = new sqlite3.Database(
path.join(app.getPath("userData"), "jan.db")
);
const query = `SELECT * FROM messages WHERE conversation_id = ${conversation_id} ORDER BY created_at DESC`;
db.all(query, (err: Error, row: any) => {
res(row);
});
db.close();
});
}
module.exports = {
init,
getConversations,
deleteConversation,
storeConversation,
storeMessage,
getConversationMessages,
storeModel,
updateFinishedDownloadAt,
getUnfinishedDownloadModels,
getFinishedDownloadModels,
deleteDownloadModel,
getModelById,
};