dtamind-components
Version:
Apps integration for Dtamind. Contain Nodes and Credentials.
240 lines • 9.92 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MySQLSaver = void 0;
const langgraph_1 = require("@langchain/langgraph");
const typeorm_1 = require("typeorm");
const utils_1 = require("../../../../src/utils");
class MySQLSaver extends langgraph_1.BaseCheckpointSaver {
constructor(config, serde) {
super(serde);
this.tableName = 'checkpoints';
this.config = config;
const { threadId } = config;
this.threadId = threadId;
}
sanitizeTableName(tableName) {
// Trim and normalize case, turn whitespace into underscores
tableName = tableName.trim().toLowerCase().replace(/\s+/g, '_');
// Validate using a regex (alphanumeric and underscores only)
if (!/^[a-zA-Z0-9_]+$/.test(tableName)) {
throw new Error('Invalid table name');
}
return tableName;
}
async getDataSource() {
const { datasourceOptions } = this.config;
if (!datasourceOptions) {
throw new Error('No datasource options provided');
}
// Prevent using default Postgres port, otherwise will throw uncaught error and crashing the app
if (datasourceOptions.port === 5432) {
throw new Error('Invalid port number');
}
const dataSource = new typeorm_1.DataSource(datasourceOptions);
await dataSource.initialize();
return dataSource;
}
async setup(dataSource) {
if (this.isSetup)
return;
try {
const queryRunner = dataSource.createQueryRunner();
const tableName = this.sanitizeTableName(this.tableName);
await queryRunner.manager.query(`
CREATE TABLE IF NOT EXISTS ${tableName} (
thread_id VARCHAR(255) NOT NULL,
checkpoint_id VARCHAR(255) NOT NULL,
parent_id VARCHAR(255),
checkpoint BLOB,
metadata BLOB,
PRIMARY KEY (thread_id, checkpoint_id)
);`);
await queryRunner.release();
}
catch (error) {
console.error(`Error creating ${this.tableName} table`, error);
throw new Error(`Error creating ${this.tableName} table`);
}
this.isSetup = true;
}
async getTuple(config) {
const dataSource = await this.getDataSource();
await this.setup(dataSource);
const thread_id = config.configurable?.thread_id || this.threadId;
const checkpoint_id = config.configurable?.checkpoint_id;
const tableName = this.sanitizeTableName(this.tableName);
try {
const queryRunner = dataSource.createQueryRunner();
const sql = checkpoint_id
? `SELECT checkpoint, parent_id, metadata FROM ${tableName} WHERE thread_id = ? AND checkpoint_id = ?`
: `SELECT thread_id, checkpoint_id, parent_id, checkpoint, metadata FROM ${tableName} WHERE thread_id = ? ORDER BY checkpoint_id DESC LIMIT 1`;
const rows = await queryRunner.manager.query(sql, checkpoint_id ? [thread_id, checkpoint_id] : [thread_id]);
await queryRunner.release();
if (rows && rows.length > 0) {
const row = rows[0];
return {
config: {
configurable: {
thread_id: row.thread_id || thread_id,
checkpoint_id: row.checkpoint_id || checkpoint_id
}
},
checkpoint: (await this.serde.parse(row.checkpoint.toString())),
metadata: (await this.serde.parse(row.metadata.toString())),
parentConfig: row.parent_id
? {
configurable: {
thread_id,
checkpoint_id: row.parent_id
}
}
: undefined
};
}
}
catch (error) {
console.error(`Error retrieving ${this.tableName}`, error);
throw new Error(`Error retrieving ${this.tableName}`);
}
finally {
await dataSource.destroy();
}
return undefined;
}
async *list(config, limit, before) {
const dataSource = await this.getDataSource();
await this.setup(dataSource);
const queryRunner = dataSource.createQueryRunner();
try {
const threadId = config.configurable?.thread_id || this.threadId;
const tableName = this.sanitizeTableName(this.tableName);
let sql = `SELECT thread_id, checkpoint_id, parent_id, checkpoint, metadata FROM ${tableName} WHERE thread_id = ? ${before ? 'AND checkpoint_id < ?' : ''} ORDER BY checkpoint_id DESC`;
if (limit) {
sql += ` LIMIT ${limit}`;
}
const args = [threadId, before?.configurable?.checkpoint_id].filter(Boolean);
const rows = await queryRunner.manager.query(sql, args);
await queryRunner.release();
if (rows && rows.length > 0) {
for (const row of rows) {
yield {
config: {
configurable: {
thread_id: row.thread_id,
checkpoint_id: row.checkpoint_id
}
},
checkpoint: (await this.serde.parse(row.checkpoint.toString())),
metadata: (await this.serde.parse(row.metadata.toString())),
parentConfig: row.parent_id
? {
configurable: {
thread_id: row.thread_id,
checkpoint_id: row.parent_id
}
}
: undefined
};
}
}
}
catch (error) {
console.error(`Error listing checkpoints`, error);
throw new Error(`Error listing checkpoints`);
}
finally {
await dataSource.destroy();
}
}
async put(config, checkpoint, metadata) {
const dataSource = await this.getDataSource();
await this.setup(dataSource);
if (!config.configurable?.checkpoint_id)
return {};
try {
const queryRunner = dataSource.createQueryRunner();
const row = [
config.configurable?.thread_id || this.threadId,
checkpoint.id,
config.configurable?.checkpoint_id,
Buffer.from(this.serde.stringify(checkpoint)), // Encode to binary
Buffer.from(this.serde.stringify(metadata)) // Encode to binary
];
const tableName = this.sanitizeTableName(this.tableName);
const query = `INSERT INTO ${tableName} (thread_id, checkpoint_id, parent_id, checkpoint, metadata)
VALUES (?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE checkpoint = VALUES(checkpoint), metadata = VALUES(metadata)`;
await queryRunner.manager.query(query, row);
await queryRunner.release();
}
catch (error) {
console.error('Error saving checkpoint', error);
throw new Error('Error saving checkpoint');
}
finally {
await dataSource.destroy();
}
return {
configurable: {
thread_id: config.configurable?.thread_id || this.threadId,
checkpoint_id: checkpoint.id
}
};
}
async delete(threadId) {
if (!threadId)
return;
const dataSource = await this.getDataSource();
await this.setup(dataSource);
const tableName = this.sanitizeTableName(this.tableName);
try {
const queryRunner = dataSource.createQueryRunner();
const query = `DELETE FROM ${tableName} WHERE thread_id = ?;`;
await queryRunner.manager.query(query, [threadId]);
await queryRunner.release();
}
catch (error) {
console.error(`Error deleting thread_id ${threadId}`, error);
}
finally {
await dataSource.destroy();
}
}
async getChatMessages(overrideSessionId = '', returnBaseMessages = false, prependMessages) {
if (!overrideSessionId)
return [];
const chatMessage = await this.config.appDataSource.getRepository(this.config.databaseEntities['ChatMessage']).find({
where: {
sessionId: overrideSessionId,
chatflowid: this.config.chatflowid
},
order: {
createdDate: 'ASC'
}
});
if (prependMessages?.length) {
chatMessage.unshift(...prependMessages);
}
if (returnBaseMessages) {
return await (0, utils_1.mapChatMessageToBaseMessage)(chatMessage, this.config.orgId);
}
let returnIMessages = [];
for (const m of chatMessage) {
returnIMessages.push({
message: m.content,
type: m.role
});
}
return returnIMessages;
}
async addChatMessages() {
// Empty as it's not being used
}
async clearChatMessages(overrideSessionId = '') {
if (!overrideSessionId)
return;
await this.delete(overrideSessionId);
}
}
exports.MySQLSaver = MySQLSaver;
//# sourceMappingURL=mysqlSaver.js.map