Update efficiency 3 pickaxes

This commit is contained in:
2026-01-14 15:56:23 +01:00
parent ce73a84459
commit cc0c682ec9
+236 -165
View File
@@ -7,6 +7,17 @@ import bodyParser from "body-parser";
import Database from "better-sqlite3"; import Database from "better-sqlite3";
import FileStoreFactory from "session-file-store"; import FileStoreFactory from "session-file-store";
import {exec} from "child_process"; import {exec} from "child_process";
import {performance} from "perf_hooks";
let last = performance.now();
setInterval(() => {
const now = performance.now();
const drift = now - last - 1000;
last = now;
if (drift > 50) {
console.log("EVENT LOOP LAG", Math.round(drift), "ms", new Date().toISOString());
}
}, 1000);
const modelState = new Map(); const modelState = new Map();
const app = express(); const app = express();
@@ -14,6 +25,18 @@ const PORT = 3000;
const DB_PATH = "./data/app.db"; const DB_PATH = "./data/app.db";
const SESSIONS_DIR = path.resolve("sessions"); const SESSIONS_DIR = path.resolve("sessions");
const UNLOAD_AFTER_MS = 30 * 1000; const UNLOAD_AFTER_MS = 30 * 1000;
let modelsCache = null;
let modelsCacheAt = 0;
const MODELS_TTL_MS = 5 * 60 * 1000;
app.use((req, res, next) => {
const start = Date.now();
res.on("finish", () => {
const ms = Date.now() - start;
if (ms > 50) console.log("SLOW REQ", ms + "ms", req.method, req.url);
});
next();
});
if (!fs.existsSync("./data")) { if (!fs.existsSync("./data")) {
fs.mkdirSync("./data", {recursive: true}); fs.mkdirSync("./data", {recursive: true});
@@ -57,212 +80,212 @@ db.prepare(`
CREATE TABLE IF NOT EXISTS users CREATE TABLE IF NOT EXISTS users
( (
username username
TEXT TEXT
PRIMARY PRIMARY
KEY, KEY,
password password
TEXT TEXT
NOT NOT
NULL NULL
) )
`).run(); `).run();
db.prepare(` db.prepare(`
CREATE TABLE IF NOT EXISTS chats CREATE TABLE IF NOT EXISTS chats
( (
id id
INTEGER INTEGER
PRIMARY PRIMARY
KEY KEY
AUTOINCREMENT, AUTOINCREMENT,
username username
TEXT TEXT
NOT NOT
NULL, NULL,
role role
TEXT TEXT
NOT NOT
NULL, NULL,
content content
TEXT TEXT
NOT NOT
NULL, NULL,
created_at created_at
DATETIME DATETIME
DEFAULT DEFAULT
CURRENT_TIMESTAMP CURRENT_TIMESTAMP
) )
`).run(); `).run();
db.prepare(` db.prepare(`
CREATE TABLE IF NOT EXISTS chat_sessions CREATE TABLE IF NOT EXISTS chat_sessions
( (
id id
INTEGER INTEGER
PRIMARY PRIMARY
KEY KEY
AUTOINCREMENT, AUTOINCREMENT,
username username
TEXT TEXT
NOT NOT
NULL, NULL,
title title
TEXT TEXT
NOT NOT
NULL, NULL,
created_at created_at
DATETIME DATETIME
DEFAULT DEFAULT
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,
updated_at updated_at
DATETIME DATETIME
DEFAULT DEFAULT
CURRENT_TIMESTAMP CURRENT_TIMESTAMP
) )
`).run(); `).run();
db.prepare(` db.prepare(`
CREATE TABLE IF NOT EXISTS chat_messages CREATE TABLE IF NOT EXISTS chat_messages
( (
id id
INTEGER INTEGER
PRIMARY PRIMARY
KEY KEY
AUTOINCREMENT, AUTOINCREMENT,
chat_id chat_id
INTEGER INTEGER
NOT NOT
NULL, NULL,
role role
TEXT TEXT
NOT NOT
NULL, NULL,
content content
TEXT TEXT
NOT NOT
NULL, NULL,
created_at created_at
DATETIME DATETIME
DEFAULT DEFAULT
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,
FOREIGN FOREIGN
KEY KEY
( (
chat_id chat_id
) REFERENCES chat_sessions ) REFERENCES chat_sessions
( (
id id
) ON DELETE CASCADE ) ON DELETE CASCADE
) )
`).run(); `).run();
db.prepare(` db.prepare(`
CREATE TABLE IF NOT EXISTS personas CREATE TABLE IF NOT EXISTS personas
( (
id id
INTEGER INTEGER
PRIMARY PRIMARY
KEY KEY
AUTOINCREMENT, AUTOINCREMENT,
username username
TEXT TEXT
NOT NOT
NULL, NULL,
name name
TEXT TEXT
NOT NOT
NULL, NULL,
pronouns pronouns
TEXT, TEXT,
appearance appearance
TEXT, TEXT,
background background
TEXT, TEXT,
details details
TEXT, TEXT,
persona_type persona_type
TEXT TEXT
DEFAULT DEFAULT
'assistant', 'assistant',
created_at created_at
DATETIME DATETIME
DEFAULT DEFAULT
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,
updated_at updated_at
DATETIME DATETIME
DEFAULT DEFAULT
CURRENT_TIMESTAMP CURRENT_TIMESTAMP
) )
`).run(); `).run();
db.prepare(` db.prepare(`
CREATE TABLE IF NOT EXISTS user_settings CREATE TABLE IF NOT EXISTS user_settings
( (
username username
TEXT TEXT
PRIMARY PRIMARY
KEY, KEY,
active_persona_id active_persona_id
INTEGER, INTEGER,
active_user_persona_id active_user_persona_id
INTEGER, INTEGER,
FOREIGN FOREIGN
KEY KEY
( (
active_persona_id active_persona_id
) REFERENCES personas ) REFERENCES personas
( (
id id
) ON DELETE SET NULL, ) ON DELETE SET NULL,
FOREIGN FOREIGN
KEY KEY
( (
active_user_persona_id active_user_persona_id
) REFERENCES personas ) REFERENCES personas
( (
id id
) ON DELETE SET NULL ) ON DELETE SET NULL
) )
`).run(); `).run();
db.prepare(` db.prepare(`
CREATE TABLE IF NOT EXISTS persona_market CREATE TABLE IF NOT EXISTS persona_market
( (
id id
INTEGER INTEGER
PRIMARY PRIMARY
KEY KEY
AUTOINCREMENT, AUTOINCREMENT,
persona_id persona_id
INTEGER, INTEGER,
creator_username creator_username
TEXT TEXT
NOT NOT
NULL, NULL,
name name
TEXT TEXT
NOT NOT
NULL, NULL,
pronouns pronouns
TEXT, TEXT,
appearance appearance
TEXT, TEXT,
background background
TEXT, TEXT,
details details
TEXT, TEXT,
persona_type persona_type
TEXT TEXT
DEFAULT DEFAULT
'assistant', 'assistant',
created_at created_at
DATETIME DATETIME
DEFAULT DEFAULT
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,
updated_at updated_at
DATETIME DATETIME
DEFAULT DEFAULT
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,
UNIQUE UNIQUE
( (
persona_id, persona_id,
creator_username creator_username
)
) )
)
`).run(); `).run();
const insertUserStmt = db.prepare("INSERT INTO users (username, password) VALUES (?, ?)"); const insertUserStmt = db.prepare("INSERT INTO users (username, password) VALUES (?, ?)");
@@ -273,7 +296,6 @@ const getLegacyChatsStmt = db.prepare(`
WHERE username = ? WHERE username = ?
ORDER BY datetime(created_at) ORDER BY datetime(created_at)
`); `);
const deleteLegacyChatsStmt = db.prepare("DELETE FROM chats WHERE username = ?");
const insertChatSessionStmt = db.prepare( const insertChatSessionStmt = db.prepare(
"INSERT INTO chat_sessions (username, title) VALUES (?, ?)" "INSERT INTO chat_sessions (username, title) VALUES (?, ?)"
); );
@@ -306,10 +328,6 @@ const deleteChatSessionStmt = db.prepare(
"DELETE FROM chat_sessions WHERE id = ? AND username = ?" "DELETE FROM chat_sessions WHERE id = ? AND username = ?"
); );
const deleteAllChatSessionsStmt = db.prepare(
"DELETE FROM chat_sessions WHERE username = ?"
);
const insertChatMessageStmt = db.prepare( const insertChatMessageStmt = db.prepare(
"INSERT INTO chat_messages (chat_id, role, content) VALUES (?, ?, ?)" "INSERT INTO chat_messages (chat_id, role, content) VALUES (?, ?, ?)"
); );
@@ -326,7 +344,8 @@ const getRecentChatMessagesStmt = db.prepare(`
SELECT role, content SELECT role, content
FROM chat_messages FROM chat_messages
WHERE chat_id = ? WHERE chat_id = ?
ORDER BY datetime(created_at) DESC LIMIT 20 ORDER BY datetime(created_at) DESC
LIMIT 20
` `
) )
; ;
@@ -336,16 +355,26 @@ const deleteChatMessagesStmt = db.prepare(
); );
const listPersonasByTypeStmt = db.prepare(` const listPersonasByTypeStmt = db.prepare(`
SELECT id, name, pronouns, appearance, background, details, persona_type, created_at, updated_at SELECT id,
name,
pronouns,
appearance,
background,
details,
persona_type,
created_at,
updated_at
FROM personas FROM personas
WHERE username = ? AND persona_type = ? WHERE username = ?
AND persona_type = ?
ORDER BY datetime(updated_at) DESC ORDER BY datetime(updated_at) DESC
`); `);
const getPersonaStmt = db.prepare(` const getPersonaStmt = db.prepare(`
SELECT id, name, pronouns, appearance, background, details, persona_type SELECT id, name, pronouns, appearance, background, details, persona_type
FROM personas FROM personas
WHERE id = ? AND username = ? WHERE id = ?
AND username = ?
`); `);
const insertPersonaStmt = db.prepare( const insertPersonaStmt = db.prepare(
@@ -354,8 +383,14 @@ const insertPersonaStmt = db.prepare(
const updatePersonaStmt = db.prepare(` const updatePersonaStmt = db.prepare(`
UPDATE personas UPDATE personas
SET name = ?, pronouns = ?, appearance = ?, background = ?, details = ?, updated_at = CURRENT_TIMESTAMP SET name = ?,
WHERE id = ? AND username = ? pronouns = ?,
appearance = ?,
background = ?,
details = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND username = ?
`); `);
const deletePersonaStmt = db.prepare( const deletePersonaStmt = db.prepare(
@@ -381,46 +416,73 @@ const setActiveUserPersonaStmt = db.prepare(`
const getActivePersonaStmt = db.prepare(` const getActivePersonaStmt = db.prepare(`
SELECT p.id, p.name, p.pronouns, p.appearance, p.background, p.details SELECT p.id, p.name, p.pronouns, p.appearance, p.background, p.details
FROM user_settings us FROM user_settings us
JOIN personas p ON p.id = us.active_persona_id JOIN personas p ON p.id = us.active_persona_id
WHERE us.username = ? WHERE us.username = ?
`); `);
const getActiveUserPersonaStmt = db.prepare(` const getActiveUserPersonaStmt = db.prepare(`
SELECT p.id, p.name, p.pronouns, p.appearance, p.background, p.details SELECT p.id, p.name, p.pronouns, p.appearance, p.background, p.details
FROM user_settings us FROM user_settings us
JOIN personas p ON p.id = us.active_user_persona_id JOIN personas p ON p.id = us.active_user_persona_id
WHERE us.username = ? WHERE us.username = ?
`); `);
const listMarketPersonasStmt = db.prepare(` const listMarketPersonasStmt = db.prepare(`
SELECT id, persona_id, creator_username, name, pronouns, appearance, background, details, persona_type, created_at, updated_at SELECT id,
persona_id,
creator_username,
name,
pronouns,
appearance,
background,
details,
persona_type,
created_at,
updated_at
FROM persona_market FROM persona_market
ORDER BY datetime(updated_at) DESC ORDER BY datetime(updated_at) DESC
`); `);
const getMarketPersonaStmt = db.prepare(` const getMarketPersonaStmt = db.prepare(`
SELECT id, persona_id, creator_username, name, pronouns, appearance, background, details, persona_type SELECT id,
persona_id,
creator_username,
name,
pronouns,
appearance,
background,
details,
persona_type
FROM persona_market FROM persona_market
WHERE id = ? WHERE id = ?
`); `);
const getMarketPersonaByPersonaIdStmt = db.prepare(` const getMarketPersonaByPersonaIdStmt = db.prepare(`
SELECT id, persona_id, creator_username, name, pronouns, appearance, background, details, persona_type SELECT id,
persona_id,
creator_username,
name,
pronouns,
appearance,
background,
details,
persona_type
FROM persona_market FROM persona_market
WHERE persona_id = ? AND creator_username = ? WHERE persona_id = ?
AND creator_username = ?
`); `);
const upsertMarketPersonaStmt = db.prepare(` const upsertMarketPersonaStmt = db.prepare(`
INSERT INTO persona_market (persona_id, creator_username, name, pronouns, appearance, background, details, persona_type) INSERT INTO persona_market (persona_id, creator_username, name, pronouns, appearance, background, details,
persona_type)
VALUES (?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(persona_id, creator_username) DO UPDATE SET ON CONFLICT(persona_id, creator_username) DO UPDATE SET name = excluded.name,
name = excluded.name, pronouns = excluded.pronouns,
pronouns = excluded.pronouns, appearance = excluded.appearance,
appearance = excluded.appearance, background = excluded.background,
background = excluded.background, details = excluded.details,
details = excluded.details, persona_type = excluded.persona_type,
persona_type = excluded.persona_type, updated_at = CURRENT_TIMESTAMP
updated_at = CURRENT_TIMESTAMP
`); `);
const listPublishedPersonaIdsStmt = db.prepare(` const listPublishedPersonaIdsStmt = db.prepare(`
@@ -475,7 +537,10 @@ app.use(bodyParser.json());
app.use(express.static("public")); app.use(express.static("public"));
app.use( app.use(
session({ session({
store: new FileStore({path: SESSIONS_DIR}), store: new FileStore({
path: SESSIONS_DIR,
reapInterval: 3600
}),
secret: "-secret-key-here-", secret: "-secret-key-here-",
resave: false, resave: false,
saveUninitialized: false, saveUninitialized: false,
@@ -548,12 +613,18 @@ const requireLogin = (req, res, next) => {
app.get("/models", async (req, res) => { app.get("/models", async (req, res) => {
try { try {
const response = await fetch("https://ai.krishd.ch/api/tags"); if (modelsCache && (Date.now() - modelsCacheAt) < MODELS_TTL_MS) {
if (!response.ok) { return res.json(modelsCache);
return res.status(500).json({error: "Failed to load models"});
} }
res.json(await response.json()); const controller = new AbortController();
} catch (error) { const t = setTimeout(() => controller.abort(), 4000);
const response = await fetch("https://ai.krishd.ch/api/tags", {signal: controller.signal});
clearTimeout(t);
if (!response.ok) return res.status(500).json({error: "Failed to load models"});
modelsCache = await response.json();
modelsCacheAt = Date.now();
res.json(modelsCache);
} catch (e) {
res.status(500).json({error: "Failed to load models"}); res.status(500).json({error: "Failed to load models"});
} }
}); });