UNPKG

@agentscope/studio

Version:

AgentScope Studio is a powerful local monitoring and visualization tool designed to provide real-time insights into your system's performance and behavior.

217 lines (216 loc) 11 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AddMessageReplyForeignKey1730000000000 = void 0; const typeorm_1 = require("typeorm"); /** * Migration: Add Reply table and establish foreign key relationship with Message * * Tasks: * 1. Create reply_table * 2. Migrate historical data: create Reply records for all replyIds * 3. Change message_table.replyId to a non-nullable foreign key */ class AddMessageReplyForeignKey1730000000000 { constructor() { this.name = 'AddMessageReplyForeignKey1730000000000'; } up(queryRunner) { return __awaiter(this, void 0, void 0, function* () { console.log('Starting migration: Adding Reply table and establishing foreign key relationship...'); // ======================================== // Step 0: Check current database state // ======================================== const messageTableExists = yield queryRunner.hasTable('message_table'); console.log(`Database state check:`); console.log(` - message_table exists: ${messageTableExists}`); // Scenario 1: First-time installation - message_table does not exist // TypeORM will create all tables from entity definitions, including foreign key relationships if (!messageTableExists) { console.log('⏭️ message_table does not exist. Skipping migration (first-time installation).'); console.log(' TypeORM will create all tables from entity definitions.'); return; } // Scenario 2: Check if migration is already completed // If foreign key constraint exists, migration has already been completed const messageTable = yield queryRunner.getTable('message_table'); const hasForeignKey = messageTable === null || messageTable === void 0 ? void 0 : messageTable.foreignKeys.some((fk) => fk.columnNames.includes('replyId') && fk.referencedTableName === 'reply_table'); if (hasForeignKey) { console.log('✅ Foreign key constraint already exists. Migration already completed.'); return; } // ======================================== // Step 1: Create reply_table // ======================================== console.log('Step 1: Creating reply_table...'); yield queryRunner.createTable(new typeorm_1.Table({ name: 'reply_table', columns: [ { name: 'replyId', // Keep camelCase, consistent with message_table type: 'varchar', isPrimary: true, }, { name: 'replyRole', type: 'varchar', }, { name: 'replyName', type: 'varchar', }, { name: 'run_id', // Keep underscore, consistent with other tables type: 'varchar', }, { name: 'createdAt', type: 'varchar', }, { name: 'finishedAt', type: 'varchar', isNullable: true, }, ], }), true); // Add foreign key for run_id yield queryRunner.createForeignKey('reply_table', new typeorm_1.TableForeignKey({ name: 'FK_reply_run', columnNames: ['run_id'], referencedTableName: 'run_table', referencedColumnNames: ['id'], onDelete: 'CASCADE', })); console.log('reply_table created successfully'); // ======================================== // Step 2: Migrate historical data // ======================================== console.log('Step 2: Migrating historical data to reply_table...'); // Query all messages (Note: column name is replyId in camelCase) const allMessages = yield queryRunner.query(`SELECT id, run_id, msg, replyId FROM message_table ORDER BY id`); console.log(`Found ${allMessages.length} messages to process`); // Collect all unique Reply information const replyMap = new Map(); let nullReplyCount = 0; for (const message of allMessages) { const msgData = typeof message.msg === 'string' ? JSON.parse(message.msg) : message.msg; const role = msgData.role || 'unknown'; const name = msgData.name || role; const timestamp = msgData.timestamp || new Date().toISOString(); const hasReplyId = message.replyId && message.replyId !== ''; const replyIdToUse = hasReplyId ? message.replyId : message.id; if (!replyMap.has(replyIdToUse)) { replyMap.set(replyIdToUse, { replyId: replyIdToUse, role, name, runId: message.run_id, createdAt: timestamp, finishedAt: timestamp, }); } else { const existing = replyMap.get(replyIdToUse); if (timestamp > existing.finishedAt) { existing.finishedAt = timestamp; existing.role = role; existing.name = name; } } if (!hasReplyId) { nullReplyCount++; } } console.log(`Need to create ${replyMap.size} Reply records, update ${nullReplyCount} messages`); // Batch insert Reply records let insertedCount = 0; for (const reply of replyMap.values()) { yield queryRunner.query(`INSERT INTO reply_table (replyId, replyRole, replyName, run_id, createdAt, finishedAt) VALUES (?, ?, ?, ?, ?, ?)`, [ reply.replyId, reply.role, reply.name, reply.runId, reply.createdAt, reply.finishedAt, ]); insertedCount++; if (insertedCount % 100 === 0) { console.log(`Progress: Inserted ${insertedCount}/${replyMap.size} Replies`); } } console.log(`Created ${insertedCount} Reply records`); // Batch update messages with NULL replyId if (nullReplyCount > 0) { console.log(`Starting to update replyId for ${nullReplyCount} messages...`); yield queryRunner.query(`UPDATE message_table SET replyId = id WHERE replyId IS NULL OR replyId = ''`); console.log(`Updated ${nullReplyCount} messages`); } // ======================================== // Step 3: Validate data integrity // ======================================== console.log('Step 3: Validating data...'); const nullCount = yield queryRunner.query(`SELECT COUNT(*) as count FROM message_table WHERE replyId IS NULL OR replyId = ''`); const count = nullCount[0].count || nullCount[0].COUNT; if (count > 0) { throw new Error(`Data migration failed: ${count} messages still have empty replyId`); } console.log('Validation passed: All messages have replyId'); // ======================================== // Step 4: Add foreign key constraint // ======================================== console.log('Step 4: Adding foreign key constraint...'); // First change column to non-nullable yield queryRunner.changeColumn('message_table', 'replyId', // Note: Keep camelCase naming new typeorm_1.TableColumn({ name: 'replyId', type: 'varchar', isNullable: false, })); // Add foreign key constraint yield queryRunner.createForeignKey('message_table', new typeorm_1.TableForeignKey({ name: 'FK_message_reply', columnNames: ['replyId'], // Column name in message_table (camelCase) referencedTableName: 'reply_table', referencedColumnNames: ['replyId'], // Column name in reply_table (camelCase) onDelete: 'CASCADE', })); console.log('Foreign key constraint added successfully'); console.log('✅ Migration completed!'); }); } down(queryRunner) { return __awaiter(this, void 0, void 0, function* () { console.log('Starting migration rollback...'); // Drop foreign key constraint const messageTable = yield queryRunner.getTable('message_table'); const foreignKey = messageTable === null || messageTable === void 0 ? void 0 : messageTable.foreignKeys.find((fk) => fk.columnNames.includes('replyId')); if (foreignKey) { yield queryRunner.dropForeignKey('message_table', foreignKey); } // Change column back to nullable yield queryRunner.changeColumn('message_table', 'replyId', new typeorm_1.TableColumn({ name: 'replyId', type: 'varchar', isNullable: true, })); // Set Message replyId to NULL yield queryRunner.query(`UPDATE message_table SET replyId = NULL`); // Drop reply_table yield queryRunner.dropTable('reply_table', true); console.log('✅ Rollback completed'); }); } } exports.AddMessageReplyForeignKey1730000000000 = AddMessageReplyForeignKey1730000000000;