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.

411 lines (410 loc) 17.7 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.RunDao = void 0; const typeorm_1 = require("typeorm"); const src_1 = require("../../../shared/src"); const Run_1 = require("../models/Run"); const RunView_1 = require("../models/RunView"); const utils_1 = require("../utils"); const Trace_1 = require("./Trace"); class RunDao { static doesProjectExist(project) { return __awaiter(this, void 0, void 0, function* () { try { const run = yield Run_1.RunTable.findOne({ where: { project } }); return run !== null; } catch (error) { console.error(error); throw error; } }); } static doesRunExist(runId) { return __awaiter(this, void 0, void 0, function* () { try { const run = yield Run_1.RunTable.findOne({ where: { id: runId } }); return run !== null; } catch (error) { console.error(error); throw error; } }); } static addRun(runData) { return __awaiter(this, void 0, void 0, function* () { try { const run = Run_1.RunTable.create(Object.assign({}, runData)); yield run.save(); } catch (error) { console.error(error); throw error; } }); } /** * Retrieve paginated projects with aggregated run statistics * * This method performs an optimized database query to fetch project data with: * - Count of runs by status (running, pending, finished) * - Total number of runs per project * - Project creation timestamp (earliest run timestamp) * - Support for pagination, sorting, and filtering * * @param input - Object containing pagination, sort, and filters * @param input.pagination - Object containing page and pageSize * @param input.pagination.page - Current page number (1-based) * @param input.pagination.pageSize - Number of items per page * @param input.sort - Optional sorting configuration * @param input.sort.field - Field to sort by (project, running, pending, finished, total, createdAt) * @param input.sort.order - Sort direction ('asc' or 'desc') * @param input.filters - Optional filters for querying * @param input.filters.project - Project name filter (uses LIKE for partial matching) * * @returns Promise resolving to TableData structure containing: * - list: Array of ProjectData objects * - total: Total number of projects (before pagination) * - page: Current page number * - pageSize: Items per page * * @throws Error if database query fails * * @example * const result = await RunDao.getProjects({ * pagination: { page: 1, pageSize: 10 }, * sort: { field: 'total', order: 'desc' }, * filters: { project: { operator: 'contains', value: 'agent' } } * }); * // Returns: { list: [...], total: 25, page: 1, pageSize: 10 } */ static getProjects(params) { return __awaiter(this, void 0, void 0, function* () { var _a; try { const { pagination, sort, filters } = params; // Build base query with aggregations let queryBuilder = Run_1.RunTable.createQueryBuilder('run') .select('run.project', 'project') .addSelect('SUM(CASE WHEN run.status = :runningStatus THEN 1 ELSE 0 END)', 'running') .addSelect('SUM(CASE WHEN run.status = :pendingStatus THEN 1 ELSE 0 END)', 'pending') .addSelect('SUM(CASE WHEN run.status = :doneStatus THEN 1 ELSE 0 END)', 'finished') .addSelect('COUNT(*)', 'total') .addSelect('MIN(run.timestamp)', 'createdAt') .groupBy('run.project') .setParameters({ runningStatus: src_1.Status.RUNNING, pendingStatus: src_1.Status.PENDING, doneStatus: src_1.Status.DONE, }); // Apply filters using HAVING (since we're using GROUP BY) if (filters === null || filters === void 0 ? void 0 : filters.project) { const filterValue = typeof filters.project === 'object' && filters.project !== null && 'value' in filters.project ? filters.project.value : String(filters.project); if (filterValue) { queryBuilder = queryBuilder.having('run.project LIKE :projectFilter', { projectFilter: `%${filterValue}%` }); } } // Apply sorting const sortField = (sort === null || sort === void 0 ? void 0 : sort.field) || 'createdAt'; const sortOrder = ((_a = sort === null || sort === void 0 ? void 0 : sort.order) === null || _a === void 0 ? void 0 : _a.toUpperCase()) === 'ASC' ? 'ASC' : 'DESC'; // For aggregated fields, we need to use the alias in quotes for SQLite switch (sortField) { case 'project': queryBuilder.orderBy('run.project', sortOrder); break; case 'running': queryBuilder.orderBy('SUM(CASE WHEN run.status = :runningStatus THEN 1 ELSE 0 END)', sortOrder); break; case 'pending': queryBuilder.orderBy('SUM(CASE WHEN run.status = :pendingStatus THEN 1 ELSE 0 END)', sortOrder); break; case 'finished': queryBuilder.orderBy('SUM(CASE WHEN run.status = :doneStatus THEN 1 ELSE 0 END)', sortOrder); break; case 'total': queryBuilder.orderBy('COUNT(*)', sortOrder); break; case 'createdAt': queryBuilder.orderBy('MIN(run.timestamp)', sortOrder); break; default: queryBuilder.orderBy('MIN(run.timestamp)', 'DESC'); } // Get total count (before pagination) const countQuery = queryBuilder.clone(); const totalResult = yield countQuery.getRawMany(); const total = totalResult.length; // Apply pagination const skip = (pagination.page - 1) * pagination.pageSize; queryBuilder.limit(pagination.pageSize).offset(skip); // Execute query const result = yield queryBuilder.getRawMany(); // Map results to ProjectData type const list = result.map((row) => ({ project: row.project, running: Number(row.running) || 0, pending: Number(row.pending) || 0, finished: Number(row.finished) || 0, total: Number(row.total) || 0, createdAt: row.createdAt, })); return { list, total, page: pagination.page, pageSize: pagination.pageSize, }; } catch (error) { console.error('Error in getProjects:', error); throw error; } }); } static getAllProjects() { return __awaiter(this, void 0, void 0, function* () { try { const result = yield Run_1.RunTable.createQueryBuilder('run') .select('DISTINCT run.project', 'project') .addSelect((qb) => qb .select('COUNT(*)') .from(Run_1.RunTable, 'r') .where('r.project = run.project') .andWhere('r.status = :running', { running: src_1.Status.RUNNING, }), 'running') .addSelect((qb) => qb .select('COUNT(*)') .from(Run_1.RunTable, 'r') .where('r.project = run.project') .andWhere('r.status = :pending', { pending: src_1.Status.PENDING, }), 'pending') .addSelect((qb) => qb .select('COUNT(*)') .from(Run_1.RunTable, 'r') .where('r.project = run.project') .andWhere('r.status = :finished', { finished: src_1.Status.DONE, }), 'finished') .addSelect((qb) => qb .select('MIN(r.timestamp)') .from(Run_1.RunTable, 'r') .where('r.project = run.project'), 'createdAt') .groupBy('run.project') .getRawMany(); return result.map((row) => ({ project: row.project, running: parseInt(row.running), pending: parseInt(row.pending), finished: parseInt(row.finished), total: parseInt(row.running) + parseInt(row.pending) + parseInt(row.finished), createdAt: row.createdAt, })); } catch (error) { console.error(error); throw error; } }); } /* * Get all runs for a project */ static getAllProjectRuns(project) { return __awaiter(this, void 0, void 0, function* () { try { const result = yield Run_1.RunTable.find({ where: { project }, order: { timestamp: 'DESC' }, }); return result.map((row) => ({ id: row.id, project: row.project, name: row.name, timestamp: row.timestamp, run_dir: row.run_dir, pid: row.pid, status: row.status, })); } catch (error) { console.error(error); throw error; } }); } static getRunData(runId) { return __awaiter(this, void 0, void 0, function* () { try { const result = yield Run_1.RunTable.findOne({ where: { id: runId }, relations: ['replies', 'replies.messages', 'inputRequests'], }); const spans = yield Trace_1.SpanDao.getSpansByConversationId(runId); if (result) { return { runData: { id: result.id, project: result.project, name: result.name, timestamp: result.timestamp, run_dir: result.run_dir, pid: result.pid, status: result.status, }, inputRequests: result.inputRequests.map((row) => ({ requestId: row.requestId, agentId: row.agentId, agentName: row.agentName, structuredInput: row.structuredInput, })), replies: result.replies.map((row) => ({ replyId: row.replyId, replyRole: row.replyRole, replyName: row.replyName, createdAt: row.createdAt, finishedAt: row.finishedAt, messages: row.messages.map((msg) => ({ id: msg.id, name: msg.msg.name, role: msg.msg.role, content: msg.msg.content, timestamp: msg.msg.timestamp, metadata: msg.msg.metadata, speech: msg.speech, })), })), spans: spans, }; } else { throw new Error(`Run with id ${runId} not found`); } } catch (error) { console.error(error); throw error; } }); } static changeRunStatus(runId, newStatus) { return __awaiter(this, void 0, void 0, function* () { try { const run = yield Run_1.RunTable.findOne({ where: { id: runId } }); if (run) { run.status = newStatus; yield run.save(); } else { throw new Error(`Run with id ${runId} not found`); } } catch (error) { console.error(error); throw error; } }); } static updateRunStatusAtBeginning() { return __awaiter(this, void 0, void 0, function* () { try { const runs = yield Run_1.RunTable.find({ where: [{ status: src_1.Status.RUNNING }, { status: src_1.Status.PENDING }], }); for (const run of runs) { const processExists = yield (0, utils_1.checkProcessByPid)(run.pid); if (!processExists) { run.status = src_1.Status.DONE; yield run.save(); } } } catch (error) { console.error(error); throw error; } }); } static getRunViewData() { return __awaiter(this, void 0, void 0, function* () { // Get run view data const runViewData = yield RunView_1.RunView.find(); // Search four projects that are updated most recently const recentProjects = yield Run_1.RunTable.createQueryBuilder('run') .select('run.project', 'project') .addSelect('MAX(run.timestamp)', 'lastUpdateTime') .addSelect('COUNT(*)', 'runCount') // 按项目分组 .groupBy('run.project') // 按最后更新时间降序排序 .orderBy('lastUpdateTime', 'DESC') // 限制返回4个结果 .limit(4) .getRawMany(); return Object.assign(Object.assign({}, runViewData[0]), { recentProjects: recentProjects.map((project) => ({ name: project.project, lastUpdateTime: project.lastUpdateTime, runCount: parseInt(project.runCount), })) }); }); } static deleteRuns(runIds) { return __awaiter(this, void 0, void 0, function* () { try { if (runIds.length > 0) { yield Trace_1.SpanDao.deleteSpansByConversationIds(runIds); } const conditions = { id: (0, typeorm_1.In)(runIds), }; const result = yield Run_1.RunTable.delete(conditions); return result.affected; } catch (error) { console.error('Error deleting runs:', error); throw error; } }); } static deleteProjects(projects) { return __awaiter(this, void 0, void 0, function* () { try { const runsToDelete = yield Run_1.RunTable.find({ where: { project: (0, typeorm_1.In)(projects) }, select: ['id'], }); const runIds = runsToDelete.map((run) => run.id); if (runIds.length > 0) { yield Trace_1.SpanDao.deleteSpansByConversationIds(runIds); } const conditions = { project: (0, typeorm_1.In)(projects), }; const result = yield Run_1.RunTable.delete(conditions); return result.affected; } catch (error) { console.error('Error deleting projects:', error); throw error; } }); } } exports.RunDao = RunDao;