Skip to main content
Glama
db-manager.js6.51 kB
/** * @file sqlite/db-manager.js * @description * Provides database initialization and management. */ import path from 'path'; import { fileURLToPath } from 'url'; import { open } from 'sqlite'; import sqlite3 from 'sqlite3'; import { getLoadablePath, load as loadSqliteVec } from 'sqlite-vec'; import { migrations } from '../../migrations/sqlite/index.js'; import { SqliteGraphRepository } from './graph-repo.js'; import { SQLiteMigrationManager } from './migration-manager.js'; const DEFAULT_DB_FILENAME = 'memory.db'; /** * Manages SQLite database connections and schema for the knowledge graph. * @class */ export class SqliteDbManager { /** * Absolute path to the SQLite database file. * @private * @type {?string} */ #dbPath = null; /** * Optional path to a custom sqlite-vec extension. * @private * @type {string|null} */ #sqliteVecPath = null; /** * The opened SQLite database instance. * @private * @type {import('sqlite').Database|null} */ #db = null; /** * Cached graph repository instance. * @type {SqliteGraphRepository|null} */ #repository = null; /** * Creates a new SqliteDbManager. * @param {string|null} [dbPath=null] * Path to the SQLite database file. If null, uses the default location. * @param {string|null} [sqliteVecPath=null] * Path to the sqlite-vec extension library. If null, loads via package. */ constructor(dbPath = null, sqliteVecPath = null) { this.#dbPath = !dbPath ? this.defaultDbPath : this.#normalizeDbPath(dbPath); this.#sqliteVecPath = sqliteVecPath; } /** * Opens or returns the existing database connection, enables foreign keys, * loads the sqlite-vec extension, and creates required tables. * @async * @returns {Promise<import('sqlite').Database>} * The initialized SQLite database instance. * @throws {Error} If loading the sqlite-vec extension fails. */ async db() { if (!this.#db) { this.#db = await open({ filename: this.#dbPath, driver: sqlite3.Database }); await this.#db.exec('PRAGMA foreign_keys=ON;'); try { if (this.#sqliteVecPath) { await this.#db.loadExtension(this.#sqliteVecPath); console.error(`Loaded sqlite-vec extension from ENV: ${this.#sqliteVecPath}`); } else { await loadSqliteVec(this.#db); console.error(`Loaded sqlite-vec extension via package (${getLoadablePath()})`); } } catch (err) { console.error('Failed to load sqlite-vec extension:', err.message); console.error('Set SQLITE_VEC_PATH=/full/path/to/vec0.so|dylib if needed'); throw err; } await this.#createTables(); await this.#applyMigrations(); } return this.#db; } /** * Returns a SQLite graph repository. * @returns {Promise<SqliteGraphRepository>} */ async graphRepository() { if (!this.#repository) { const db = await this.db(); this.#repository = new SqliteGraphRepository(db); } return this.#repository; } /** * Returns the default database file path located next to this module. * @type {string} */ get defaultDbPath() { return path.join(path.dirname(fileURLToPath(import.meta.url)), DEFAULT_DB_FILENAME); } /** * Creates the necessary tables and virtual tables for the knowledge graph. * @returns {Promise<void>} * @private */ #createTables() { return this.#db.exec(` CREATE TABLE IF NOT EXISTS entities ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE NOT NULL, entityType TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS observations ( id INTEGER PRIMARY KEY AUTOINCREMENT, entity_id INTEGER NOT NULL REFERENCES entities ( id ) ON DELETE CASCADE, content TEXT NOT NULL, UNIQUE ( entity_id, content ) ); CREATE TABLE IF NOT EXISTS relations ( id INTEGER PRIMARY KEY AUTOINCREMENT, from_id INTEGER NOT NULL REFERENCES entities ( id ) ON DELETE CASCADE, to_id INTEGER NOT NULL REFERENCES entities ( id ) ON DELETE CASCADE, relationType TEXT NOT NULL, UNIQUE ( from_id, to_id, relationType ) ); CREATE VIRTUAL TABLE IF NOT EXISTS obs_vec USING vec0(entity_id INT, embedding FLOAT[1024]); `); } /** * Apply database migrations. * @returns {Promise<void>} * @private */ async #applyMigrations() { const migrationManager = new SQLiteMigrationManager(this.#db); await migrationManager.initialize(); return migrationManager.migrate(migrations, null, true); } /** * Converts a relative or absolute database path to an absolute path. * @private * @param {string} dbPath - The input database path. * @returns {string} The normalized absolute database path. */ #normalizeDbPath(dbPath) { return path.isAbsolute(dbPath) ? dbPath : path.join(path.dirname(fileURLToPath(import.meta.url)), dbPath); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/iAchilles/memento'

If you have feedback or need assistance with the MCP directory API, please join our Discord server