@oliverlessa/gemini-agent-lib
Version:
Uma biblioteca NodeJS para criar agentes de IA com Gemini LLM
165 lines (154 loc) • 6.91 kB
JavaScript
const SummaryMemory = require('./summary-memory');
const { MongoClient } = require('mongodb');
const debug = require('../debug').create('memory:mongodb');
/**
* Adaptador de memória de resumos que utiliza MongoDB para persistência.
* Implementa a interface SummaryMemory.
*/
class MongoDBSummaryMemoryAdapter extends SummaryMemory {
/**
* Construtor para o adaptador de memória de resumos MongoDB.
* @param {Object} options - Opções de configuração.
* @param {Object} options.dbConfig - Configuração do banco de dados MongoDB.
* @param {string} options.dbConfig.connectionUri - URI de conexão do MongoDB.
* @param {string} options.dbConfig.dbName - Nome do banco de dados MongoDB.
* @param {string} [options.dbConfig.collectionName='summaries'] - Nome da collection para os resumos.
*/
constructor({ dbConfig }) {
if (!dbConfig || !dbConfig.connectionUri) {
throw new Error("MongoDBSummaryMemoryAdapter requer 'dbConfig.connectionUri'.");
}
if (!dbConfig || !dbConfig.dbName) {
throw new Error("MongoDBSummaryMemoryAdapter requer 'dbConfig.dbName'.");
}
super({ dbType: 'mongodb', dbConfig });
this.connectionUri = dbConfig.connectionUri;
this.dbName = dbConfig.dbName;
this.collectionName = dbConfig.collectionName || 'summaries';
this.client = new MongoClient(this.connectionUri);
this.summariesCollection = null;
}
/**
* Conecta ao banco de dados MongoDB e obtém a collection de resumos.
* Deve ser chamado e aguardado antes de usar o adaptador.
* @returns {Promise<void>}
*/
async initialize() {
try {
await this.client.connect();
const db = this.client.db(this.dbName);
this.summariesCollection = db.collection(this.collectionName);
// Criar índice para buscas eficientes por timestamp DESC (operação idempotente - não causa erro se o índice já existir)
await this.summariesCollection.createIndex({ context_id: 1, timestamp: -1 }, { background: true });
debug(`Conectado ao MongoDB, database: '${this.dbName}', collection: '${this.collectionName}'`);
} catch (error) {
console.error("[MongoDBSummaryMemoryAdapter] Erro ao conectar ao MongoDB:", error);
throw error;
}
}
/**
* Adiciona um novo resumo para um contexto específico.
* @param {string} contextId - Identificador único do contexto.
* @param {string} summaryContent - O conteúdo do resumo.
* @param {Date} [timestamp=new Date()] - O timestamp associado ao resumo.
* @returns {Promise<void>}
*/
async addSummary(contextId, summaryContent, timestamp = new Date()) {
try {
if (!this.summariesCollection) {
throw new Error("[MongoDBSummaryMemoryAdapter] Não conectado ao MongoDB.");
}
await this.summariesCollection.insertOne({
context_id: contextId,
summary_content: summaryContent,
timestamp: timestamp // Armazena como tipo Date do BSON
});
} catch (error) {
console.error("[MongoDBSummaryMemoryAdapter] Erro ao adicionar resumo:", error);
throw error;
}
}
/**
* Recupera o resumo mais recente para um contexto específico.
* @param {string} contextId - Identificador único do contexto.
* @returns {Promise<string|null>} - O conteúdo do resumo mais recente, ou null se nenhum resumo for encontrado.
*/
async getLatestSummary(contextId) {
try {
if (!this.summariesCollection) {
throw new Error("[MongoDBSummaryMemoryAdapter] Não conectado ao MongoDB.");
}
const latestDoc = await this.summariesCollection.findOne(
{ context_id: contextId },
{ sort: { timestamp: -1 } } // Ordena por timestamp descendente e pega o primeiro
);
return latestDoc ? latestDoc.summary_content : null;
} catch (error) {
console.error("[MongoDBSummaryMemoryAdapter] Erro ao obter último resumo:", error);
throw error;
}
}
/**
* Recupera todos os resumos para um contexto específico, ordenados do mais recente para o mais antigo.
* @param {string} contextId - Identificador único do contexto.
* @param {number} [limit] - Número máximo de resumos a serem retornados.
* @returns {Promise<Array<{ summaryContent: string, timestamp: Date }>>} - Um array de objetos de resumo.
*/
async getAllSummaries(contextId, limit) {
try {
if (!this.summariesCollection) {
throw new Error("[MongoDBSummaryMemoryAdapter] Não conectado ao MongoDB.");
}
let queryOptions = { sort: { timestamp: -1 } }; // Ordena por timestamp descendente
if (limit && typeof limit === 'number' && limit > 0) {
queryOptions.limit = limit;
}
const cursor = this.summariesCollection.find({ context_id: contextId }, queryOptions);
const summaries = [];
await cursor.forEach(doc => {
summaries.push({
summaryContent: doc.summary_content,
timestamp: doc.timestamp // Timestamp já é Date
});
});
return summaries;
} catch (error) {
console.error("[MongoDBSummaryMemoryAdapter] Erro ao obter todos os resumos:", error);
throw error;
}
}
/**
* Deleta todos os resumos para um contexto específico.
* @param {string} contextId - Identificador único do contexto.
* @returns {Promise<void>}
*/
async deleteAllSummaries(contextId) {
try {
if (!this.summariesCollection) {
throw new Error("[MongoDBSummaryMemoryAdapter] Não conectado ao MongoDB.");
}
await this.summariesCollection.deleteMany({ context_id: contextId });
} catch (error) {
console.error("[MongoDBSummaryMemoryAdapter] Erro ao deletar resumos:", error);
throw error;
}
}
/**
* Fecha a conexão com o banco de dados MongoDB.
* @returns {Promise<void>}
*/
async close() {
if (this.client) {
try {
await this.client.close();
debug("Conexão com o banco de dados MongoDB fechada.");
this.client = null;
this.summariesCollection = null;
} catch (error) {
console.error("[MongoDBSummaryMemoryAdapter] Erro ao fechar conexão MongoDB:", error);
throw error;
}
}
}
}
module.exports = MongoDBSummaryMemoryAdapter;