Merge Memory V2 runtime facade
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
const DEFAULT_TABLE = 'memory_embeddings';
|
||||
const DEFAULT_DIMENSIONS = 1536;
|
||||
|
||||
function isSafeIdentifier(value) {
|
||||
return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(String(value ?? ''));
|
||||
}
|
||||
|
||||
function quoteIdent(value) {
|
||||
const normalized = String(value ?? '').trim();
|
||||
if (!isSafeIdentifier(normalized)) {
|
||||
throw new Error(`Invalid PostgreSQL identifier: ${normalized}`);
|
||||
}
|
||||
return `"${normalized}"`;
|
||||
}
|
||||
|
||||
function resolveDimensions(value) {
|
||||
const dimensions = Number(value ?? DEFAULT_DIMENSIONS);
|
||||
if (!Number.isInteger(dimensions) || dimensions < 1 || dimensions > 16_384) {
|
||||
throw new Error(`Invalid pgvector dimensions: ${value}`);
|
||||
}
|
||||
return dimensions;
|
||||
}
|
||||
|
||||
export function buildPgvectorMemorySchemaSql({
|
||||
tableName = DEFAULT_TABLE,
|
||||
dimensions = DEFAULT_DIMENSIONS,
|
||||
createExtension = false,
|
||||
createVectorIndex = false,
|
||||
} = {}) {
|
||||
const table = quoteIdent(tableName);
|
||||
const dim = resolveDimensions(dimensions);
|
||||
const statements = [];
|
||||
if (createExtension) {
|
||||
statements.push('CREATE EXTENSION IF NOT EXISTS vector');
|
||||
}
|
||||
statements.push(`
|
||||
CREATE TABLE IF NOT EXISTS ${table} (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
embedding vector(${dim}) NOT NULL,
|
||||
type TEXT NOT NULL DEFAULT 'fact',
|
||||
source_memory_id TEXT,
|
||||
source_session_id TEXT,
|
||||
source_message_id TEXT,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)`.trim());
|
||||
statements.push(
|
||||
`CREATE INDEX IF NOT EXISTS ${quoteIdent(`${tableName}_user_created_idx`)} ON ${table} (user_id, created_at DESC)`,
|
||||
);
|
||||
statements.push(
|
||||
`CREATE UNIQUE INDEX IF NOT EXISTS ${quoteIdent(`${tableName}_source_memory_id_uidx`)} ON ${table} (source_memory_id) WHERE source_memory_id IS NOT NULL`,
|
||||
);
|
||||
if (createVectorIndex) {
|
||||
statements.push(
|
||||
`CREATE INDEX IF NOT EXISTS ${quoteIdent(`${tableName}_embedding_idx`)} ON ${table} USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100)`,
|
||||
);
|
||||
}
|
||||
return statements;
|
||||
}
|
||||
|
||||
export async function ensurePgvectorMemorySchema(pool, options = {}) {
|
||||
if (!pool?.query) {
|
||||
throw new Error('ensurePgvectorMemorySchema requires a PostgreSQL pool with query(sql)');
|
||||
}
|
||||
const statements = buildPgvectorMemorySchemaSql(options);
|
||||
for (const statement of statements) {
|
||||
await pool.query(statement);
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
statements: statements.length,
|
||||
tableName: options.tableName ?? DEFAULT_TABLE,
|
||||
dimensions: resolveDimensions(options.dimensions),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user