132 lines
4.8 KiB
JavaScript
132 lines
4.8 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.MessagesDAO = void 0;
|
|
const uuid_1 = require("uuid");
|
|
const vector_utils_1 = require("../core/vector-utils");
|
|
const vector_search_engine_1 = require("../core/vector-search-engine");
|
|
class MessagesDAO {
|
|
dbManager;
|
|
embeddingService;
|
|
searchEngine;
|
|
constructor(dbManager, embeddingService) {
|
|
this.dbManager = dbManager;
|
|
this.embeddingService = embeddingService;
|
|
this.searchEngine = new vector_search_engine_1.VectorSearchEngine();
|
|
}
|
|
get db() {
|
|
return this.dbManager.getDatabase();
|
|
}
|
|
async create(input) {
|
|
const id = input.id || (0, uuid_1.v4)();
|
|
const now = input.timestamp || new Date();
|
|
// Generate embedding (Async)
|
|
let embedding;
|
|
let embeddingBlob = null;
|
|
let embeddingModel = null;
|
|
let embeddingGeneratedAt = null;
|
|
try {
|
|
embedding = await this.embeddingService.embed(input.content);
|
|
if (embedding) {
|
|
embeddingBlob = (0, vector_utils_1.serializeEmbedding)(embedding);
|
|
const modelInfo = this.embeddingService.getModelInfo();
|
|
embeddingModel = modelInfo.name;
|
|
embeddingGeneratedAt = Date.now();
|
|
}
|
|
}
|
|
catch (e) {
|
|
console.error('Failed to generate embedding:', e);
|
|
// Proceed without embedding
|
|
}
|
|
const message = {
|
|
id,
|
|
threadId: input.threadId,
|
|
role: input.role,
|
|
content: input.content,
|
|
timestamp: now,
|
|
metadata: input.metadata || {},
|
|
embedding
|
|
};
|
|
const stmt = this.db.prepare(`
|
|
INSERT INTO messages (
|
|
id, thread_id, role, content, timestamp, metadata,
|
|
embedding_blob, embedding_model, embedding_generated_at
|
|
) VALUES (
|
|
?, ?, ?, ?, ?, ?, ?, ?, ?
|
|
)
|
|
`);
|
|
stmt.run(message.id, message.threadId, message.role, message.content, message.timestamp.getTime(), JSON.stringify(message.metadata || {}), embeddingBlob, embeddingModel, embeddingGeneratedAt);
|
|
// Update thread message count
|
|
this.updateThreadMessageCount(message.threadId);
|
|
return message;
|
|
}
|
|
findByThreadId(threadId, options) {
|
|
const { limit = 50, offset = 0 } = options || {};
|
|
const stmt = this.db.prepare(`
|
|
SELECT * FROM messages
|
|
WHERE thread_id = ?
|
|
ORDER BY timestamp DESC
|
|
LIMIT ? OFFSET ?
|
|
`);
|
|
const rows = stmt.all(threadId, limit, offset);
|
|
return rows.map(this.mapRowToMessage);
|
|
}
|
|
/**
|
|
* Semantically search for messages
|
|
*/
|
|
async searchSimilar(query, options = {}) {
|
|
const { threadId, topK = 10, minScore = 0.5 } = options;
|
|
// 1. Generate query vector
|
|
const queryVector = await this.embeddingService.embed(query);
|
|
// 2. Load candidate messages (all messages with embeddings)
|
|
// Optimization: Filter by threadId in SQL if provided to reduce memory usage
|
|
let sql = 'SELECT * FROM messages WHERE embedding_blob IS NOT NULL';
|
|
const params = [];
|
|
if (threadId) {
|
|
sql += ' AND thread_id = ?';
|
|
params.push(threadId);
|
|
}
|
|
const rows = this.db.prepare(sql).all(...params);
|
|
// 3. Build corpus
|
|
const corpus = rows.map(row => ({
|
|
id: row.id,
|
|
vector: (0, vector_utils_1.deserializeEmbedding)(row.embedding_blob),
|
|
payload: this.mapRowToMessage(row)
|
|
}));
|
|
// 4. Perform vector search
|
|
const results = this.searchEngine.search(queryVector, corpus, {
|
|
topK,
|
|
minScore
|
|
});
|
|
// 5. Map results
|
|
return results.map(result => ({
|
|
...result.payload,
|
|
score: result.score
|
|
}));
|
|
}
|
|
deleteByThreadId(threadId) {
|
|
const stmt = this.db.prepare('DELETE FROM messages WHERE thread_id = ?');
|
|
stmt.run(threadId);
|
|
}
|
|
updateThreadMessageCount(threadId) {
|
|
const countStmt = this.db.prepare('SELECT COUNT(*) as count FROM messages WHERE thread_id = ?');
|
|
const result = countStmt.get(threadId);
|
|
const updateStmt = this.db.prepare('UPDATE threads SET message_count = ? WHERE id = ?');
|
|
updateStmt.run(result.count, threadId);
|
|
}
|
|
mapRowToMessage(row) {
|
|
const message = {
|
|
id: row.id,
|
|
threadId: row.thread_id,
|
|
role: row.role,
|
|
content: row.content,
|
|
timestamp: new Date(row.timestamp),
|
|
metadata: JSON.parse(row.metadata || '{}')
|
|
};
|
|
if (row.embedding_blob) {
|
|
message.embedding = (0, vector_utils_1.deserializeEmbedding)(row.embedding_blob);
|
|
}
|
|
return message;
|
|
}
|
|
}
|
|
exports.MessagesDAO = MessagesDAO;
|