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.

603 lines (602 loc) 27.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); 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()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SocketManager = void 0; exports.runPythonScript = runPythonScript; const child_process_1 = require("child_process"); const socket_io_1 = require("socket.io"); const messageForm_1 = require("../../../shared/src/types/messageForm"); const trpc_1 = require("../../../shared/src/types/trpc"); const Run_1 = require("../dao/Run"); const dayjs_1 = __importDefault(require("dayjs")); const fs = __importStar(require("node:fs")); const src_1 = require("../../../shared/src"); const friday_1 = require("../../../shared/src/config/friday"); const FridayAppMessage_1 = require("../dao/FridayAppMessage"); const InputRequest_1 = require("../dao/InputRequest"); const Trace_1 = require("../dao/Trace"); const ReplyingStateManager_1 = require("../services/ReplyingStateManager"); class SocketManager { static close() { if (this.io) { this.io.close(); } } static sendInterruptSignalToFriday() { return __awaiter(this, void 0, void 0, function* () { this.io.of('/friday').emit(trpc_1.SocketEvents.server.interruptReply); }); } static init(httpServer) { this.io = new socket_io_1.Server(httpServer, { cors: { origin: '*', }, maxHttpBufferSize: Infinity, }); const fridayNamespace = this.io.of('/friday'); fridayNamespace.on('connection', (socket) => { console.debug(`${socket.id}: Friday app client connected`); socket.on('disconnect', () => { console.debug(`${socket.id}: Friday app client disconnected`); }); }); // Python client connection const pythonNamespace = this.io.of('/python'); pythonNamespace.on('connection', (socket) => { const runId = socket.handshake.auth.run_id; console.debug(`${socket.id}: Python client connected`); socket.on('disconnect', () => __awaiter(this, void 0, void 0, function* () { // Delete the input requests yield InputRequest_1.InputRequestDao.deleteInputRequestsByRunId(runId); this.changeRunStatusAndTriggerEvents(runId, messageForm_1.Status.DONE).catch((error) => { console.error(error); throw error; }); })); }); const clientNamespace = this.io.of('/client'); clientNamespace.on('connection', (socket) => { console.debug('Client connected'); socket.on(trpc_1.SocketEvents.client.joinProjectListRoom, () => { socket.join(trpc_1.SocketRoomName.ProjectListRoom); console.debug(`${socket.id}: joined room: ${trpc_1.SocketRoomName.ProjectListRoom}`); Run_1.RunDao.getAllProjects() .then((projects) => { // Push projects to the client socket.emit(trpc_1.SocketEvents.server.pushProjects, projects); }) .catch((error) => { console.error(error); throw error; }); }); socket.on(trpc_1.SocketEvents.client.joinProjectRoom, (project, callback) => __awaiter(this, void 0, void 0, function* () { const projectExist = yield Run_1.RunDao.doesProjectExist(project); if (!projectExist) { callback({ success: false, message: `Project ${project} not found`, }); } else { socket.join(`project-${project}`); console.debug(`${socket.id}: joined room: project-${project}`); // Return runs to this socket/client Run_1.RunDao.getAllProjectRuns(project) .then((runs) => { // Push runs to the client socket.emit(trpc_1.SocketEvents.server.pushRunsData, runs); }) .catch((error) => { console.error(error); throw error; }); } })); socket.on(trpc_1.SocketEvents.client.joinRunRoom, (runId, callback) => __awaiter(this, void 0, void 0, function* () { const runExist = yield Run_1.RunDao.doesRunExist(runId); if (!runExist) { callback({ success: false, message: `Run ${runId} not found`, }); } else { socket.join(`run-${runId}`); console.debug(`${socket.id}: joined room: run-${runId}`); // Return run data, input requests and messages to this socket/client Run_1.RunDao.getRunData(runId) .then((data) => { socket.emit(trpc_1.SocketEvents.server.pushRunData, data.runData); socket.emit(trpc_1.SocketEvents.server.pushInputRequests, data.inputRequests); // 对data.replies.messages按时间排序 data.replies.forEach((reply) => { reply.messages.sort((a, b) => { return a.timestamp.localeCompare(b.timestamp); }); }); socket.emit(trpc_1.SocketEvents.server.pushMessages, data.replies); socket.emit(trpc_1.SocketEvents.server.pushSpans, data.spans); }) .catch((error) => { console.error(error); throw error; }); // Return model invocation data Trace_1.SpanDao.getModelInvocationData(runId).then((data) => { socket.emit(trpc_1.SocketEvents.server.pushModelInvocationData, data); }); } })); socket.on(trpc_1.SocketEvents.client.sendUserInputToServer, (requestId, blocksInput, structuredInput, callback) => __awaiter(this, void 0, void 0, function* () { const inputRequest = yield InputRequest_1.InputRequestDao.getInputRequestByRequestId(requestId); if (inputRequest === null) { callback({ success: false, message: `Input request ${requestId} not found`, }); } else { const runId = inputRequest.runId; yield InputRequest_1.InputRequestDao.deleteInputRequest(requestId); // If input requests are empty, change the run status to finished const res = yield Run_1.RunDao.getRunData(runId); if (res.inputRequests.length === 0) { this.changeRunStatusAndTriggerEvents(runId, messageForm_1.Status.RUNNING).catch((error) => { console.error(error); throw error; }); } // Emit the input to the python client this.io .of('/python') .emit(trpc_1.SocketEvents.server.forwardUserInput, requestId, blocksInput, structuredInput); } })); socket.on(trpc_1.SocketEvents.client.joinOverviewRoom, () => __awaiter(this, void 0, void 0, function* () { socket.join(trpc_1.SocketRoomName.OverviewRoom); console.debug(`${socket.id}: joined room: ${trpc_1.SocketRoomName.OverviewRoom}`); // Return current overview data const res = yield this._getOverViewData(); socket.emit(trpc_1.SocketEvents.server.pushOverviewData, res); })); socket.on(trpc_1.SocketEvents.client.leaveRoom, (room) => { socket.leave(room); console.debug(`${socket.id}: left room: ${room}`); }); socket.on(trpc_1.SocketEvents.client.deleteProjects, (projects, callback) => __awaiter(this, void 0, void 0, function* () { try { yield Run_1.RunDao.deleteProjects(projects); callback({ success: true, message: `Success: ${projects.length} project deleted`, }); // Update projectListRoom, overviewRoom, this.broadcastOverviewDataToDashboardRoom(); this.broadcastRunToProjectListRoom(); } catch (error) { callback({ success: false, message: `Error: ${error}`, }); } })); socket.on(trpc_1.SocketEvents.client.deleteRuns, (runIds, callback) => __awaiter(this, void 0, void 0, function* () { try { const nDelete = yield Run_1.RunDao.deleteRuns(runIds); callback({ success: nDelete === runIds.length, message: `Deleted ${nDelete} runs`, }); // Update data to overviewRoom, projectRoom this.broadcastOverviewDataToDashboardRoom(); this.broadcastRunToProjectListRoom(); } catch (error) { callback({ success: false, message: `Failed to delete runs: ${error}`, }); } })); // friday app socket.on(trpc_1.SocketEvents.client.getFridayConfig, (callback) => __awaiter(this, void 0, void 0, function* () { console.debug(`${socket.id}: getFridayConfig`); try { // Send config to the client const fridayConfig = friday_1.FridayConfigManager.getInstance().getConfig(); callback({ success: true, data: fridayConfig, message: 'Get Friday config successfully', }); } catch (error) { console.error(error); callback({ success: false, data: null, message: `Failed to get Friday config: ${error}`, }); } })); socket.on(trpc_1.SocketEvents.client.saveFridayConfig, (config, callback) => __awaiter(this, void 0, void 0, function* () { const fridayConfigManager = friday_1.FridayConfigManager.getInstance(); // Save the config to the file fridayConfigManager.updateConfig(config); callback({ success: true, message: 'Save Friday config successfully', }); })); socket.on(trpc_1.SocketEvents.client.installFridayRequirements, (pythonEnv, callback) => __awaiter(this, void 0, void 0, function* () { const fridayConfigManager = friday_1.FridayConfigManager.getInstance(); const res = yield fridayConfigManager.installRequirements(pythonEnv); callback(res); })); socket.on(trpc_1.SocketEvents.client.joinFridayAppRoom, (callback) => __awaiter(this, void 0, void 0, function* () { console.debug(`${socket.id}: joined room: ${trpc_1.SocketRoomName.FridayAppRoom}`); socket.join(trpc_1.SocketRoomName.FridayAppRoom); const replyingManager = ReplyingStateManager_1.ReplyingStateManager.getInstance(); // Push the replying state socket.emit(trpc_1.SocketEvents.server.pushReplyingState, replyingManager.getReplyingState()); // Push the replies to the client FridayAppMessage_1.FridayAppMessageDao.getRepliesBefore(undefined, 100) .then((res) => { socket.emit(trpc_1.SocketEvents.server.pushReplies, res.replies, res.hasMore); }) .catch((error) => { console.error(error); callback({ success: false, message: `Failed to get replies: ${error}`, }); }); })); socket.on(trpc_1.SocketEvents.client.verifyFridayConfig, (pythonEnv, callback) => __awaiter(this, void 0, void 0, function* () { console.debug(`${socket.id}: verifyPythonEnv:`, pythonEnv); // Verify if python exists const result = friday_1.FridayConfigManager.getInstance().verifyPythonEnv(pythonEnv); callback(result); })); socket.on(trpc_1.SocketEvents.client.sendUserInputToFridayApp, (name, role, content, callback) => __awaiter(this, void 0, void 0, function* () { const replyingManager = ReplyingStateManager_1.ReplyingStateManager.getInstance(); const replyId = crypto.randomUUID(); const msgId = crypto.randomUUID(); FridayAppMessage_1.FridayAppMessageDao.saveReplyMessage(replyId, { id: msgId, name: name, role: role, content: content, metadata: {}, timestamp: (0, dayjs_1.default)().format('YYYY-MM-DD HH:mm:ss.SSS'), }, true) .then((reply) => { this.broadcastReplyToFridayAppRoom(reply); }) .catch((error) => { console.error(error); callback({ success: false, message: `Failed to add reply: ${error}`, }); }); // Send the message to the python client // TODO: move somewhere const config = src_1.ConfigManager.getInstance().getConfig(); // Broad the replying state to the Friday app room replyingManager.setReplyingState(true); this.broadcastReplyingStateToFridayAppRoom(); const fridayConfigManager = friday_1.FridayConfigManager.getInstance(); const fridayConfig = fridayConfigManager.getConfig(); if (!fridayConfig) { callback({ success: false, message: 'Friday config not found. Please set it up first.', }); return; } // Prepare the arguments for the Python script const mainScriptPath = fridayConfig.mainScriptPath ? fridayConfig.mainScriptPath : fridayConfigManager.getDefaultMainScriptPath(); const args = [ mainScriptPath, '--query', JSON.stringify(content), '--studio_url', `http://localhost:${config.port}`, ]; console.debug(fridayConfig); for (const [key, value] of Object.entries(fridayConfig)) { if (key !== 'pythonEnv' && key !== 'mainScriptPath') { if (typeof value === 'object') { args.push(`--${key}`, JSON.stringify(value)); } else { args.push(`--${key}`, value); } } } runPythonScript(fridayConfig.pythonEnv, args) .then((result) => { if (result.success) { console.debug('[PYTHON SCRIPT OUTPUT]:', result.data); } else { console.error('[PYTHON SCRIPT ERROR]:', result.error); callback({ success: false, message: result.error, }); } }) .catch((error) => { console.error('[PYTHON SCRIPT ERROR]:', error); callback({ success: false, message: error, }); }) .finally(() => { replyingManager.setReplyingState(false); this.broadcastReplyingStateToFridayAppRoom(); }); })); socket.on(trpc_1.SocketEvents.client.interruptReplyOfFridayApp, () => __awaiter(this, void 0, void 0, function* () { yield this.sendInterruptSignalToFriday(); })); socket.on(trpc_1.SocketEvents.client.cleanHistoryOfFridayApp, () => __awaiter(this, void 0, void 0, function* () { FridayAppMessage_1.FridayAppMessageDao.cleanHistoryMessages().then(() => { const filePath = src_1.PATHS.getFridayDialogHistoryPath(); if (fs.existsSync(filePath)) { fs.unlinkSync(filePath); console.debug(`Deleted file: ${filePath}`); } else { console.warn(`File not found: ${filePath}`); } // TODO: 告知client this.broadcastReplyToFridayAppRoom(undefined, true); }); })); socket.on('disconnect', () => { console.debug('Client disconnected'); }); }); } /* * Emit events to the project list room. */ static broadcastRunToProjectListRoom() { Run_1.RunDao.getAllProjects() .then((projects) => { // Push projects to the client this.io .of('/client') .to(trpc_1.SocketRoomName.ProjectListRoom) .emit(trpc_1.SocketEvents.server.pushProjects, projects); }) .catch((error) => { console.error(error); throw error; }); } static broadcastRunToProjectRoom(project) { Run_1.RunDao.getAllProjectRuns(project) .then((runs) => { // Push runs to the client this.io .of('/client') .to(`project-${project}`) .emit(trpc_1.SocketEvents.server.pushRunsData, runs); }) .catch((error) => { console.error(error); throw error; }); } /* * Emit events to the run room. */ static broadcastMessageToRunRoom(runId, reply) { this.io .of('/client') .to(`run-${runId}`) .emit(trpc_1.SocketEvents.server.pushMessages, [reply]); } /** * Broadcast speech data to the run room for real-time audio playback */ static broadcastSpeechToRunRoom(runId, replyId, speech) { const speechData = { replyId, speech, }; this.io .of('/client') .to(`run-${runId}`) .emit(trpc_1.SocketEvents.server.pushSpeech, speechData); } static broadcastSpanDataToRunRoom(spanDataArray) { // Group spans by runId const groupedSpans = {}; spanDataArray.forEach((spanData) => { if (!groupedSpans[spanData.conversationId]) { groupedSpans[spanData.conversationId] = []; } groupedSpans[spanData.conversationId].push(spanData); }); // Send grouped spans to each run room for (const runId in groupedSpans) { this.io .of('/client') .to(`run-${runId}`) .emit(trpc_1.SocketEvents.server.pushSpans, groupedSpans[runId]); this.broadcastModelInvocationDataToRunRoom(runId); } } static broadcastInputRequestToRunRoom(runId, inputRequest) { this.io .of('/client') .to(`run-${runId}`) .emit(trpc_1.SocketEvents.server.pushInputRequests, [inputRequest]); } static broadcastRunDataToRunRoom(runId, runData) { this.io .of('/client') .to(`run-${runId}`) .emit(trpc_1.SocketEvents.server.pushRunData, runData); } static clearInputRequestsToRunRoom(runId) { this.io .of('/client') .to(`run-${runId}`) .emit(trpc_1.SocketEvents.server.clearInputRequests); } static changeRunStatusAndTriggerEvents(runId, newStatus) { return __awaiter(this, void 0, void 0, function* () { const runExist = yield Run_1.RunDao.doesRunExist(runId); if (runExist) { // Update the run status to "finished" yield Run_1.RunDao.changeRunStatus(runId, newStatus); // Find the project by runId const res = yield Run_1.RunDao.getRunData(runId); const project = res.runData.project; // Broadcast projects to all clients in the ProjectList room this.broadcastRunToProjectListRoom(); // Broadcast runs to all clients in the project room this.broadcastRunToProjectRoom(project); // Broadcast run data to all clients in the run room this.broadcastRunDataToRunRoom(runId, res.runData); if (newStatus === messageForm_1.Status.DONE) { // Clear the input requests for all clients in the run room this.clearInputRequestsToRunRoom(runId); } } }); } static broadcastOverviewDataToDashboardRoom() { this._getOverViewData() .then((res) => { this.io .of('/client') .to(trpc_1.SocketRoomName.OverviewRoom) .emit(trpc_1.SocketEvents.server.pushOverviewData, res); }) .catch((error) => { console.error(error); throw error; }); } static _getOverViewData() { return __awaiter(this, void 0, void 0, function* () { const res1 = yield Run_1.RunDao.getRunViewData(); const res2 = yield Trace_1.SpanDao.getModelInvocationViewData(); return Object.assign(Object.assign({}, res1), res2); }); } static broadcastModelInvocationDataToRunRoom(runId) { Trace_1.SpanDao.getModelInvocationData(runId).then((data) => { this.io .of('/client') .to(`run-${runId}`) .emit(trpc_1.SocketEvents.server.pushModelInvocationData, data); }); } static broadcastReplyToFridayAppRoom(reply, override = false) { this.io .of('/client') .to(trpc_1.SocketRoomName.FridayAppRoom) .emit(trpc_1.SocketEvents.server.pushReplies, reply === undefined ? [] : [reply], false, override); } static broadcastReplyingStateToFridayAppRoom() { const replyingManager = ReplyingStateManager_1.ReplyingStateManager.getInstance(); this.io .of('/client') .to(trpc_1.SocketRoomName.FridayAppRoom) .emit(trpc_1.SocketEvents.server.pushReplyingState, replyingManager.getReplyingState()); } } exports.SocketManager = SocketManager; function runPythonScript(pythonEnv, commands) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve) => { console.debug('The execute command:', pythonEnv, commands); const pythonProcess = (0, child_process_1.spawn)(pythonEnv, commands, { env: Object.assign(Object.assign({}, process.env), { FORCE_COLOR: '0' }), }); let output = ''; let errorOutput = ''; // 收集标准输出 pythonProcess.stdout.on('data', (data) => { output += data.toString(); }); // 收集错误输出 pythonProcess.stderr.on('data', (data) => { errorOutput += data.toString(); }); // 进程结束时处理结果 pythonProcess.on('close', (code) => { if (code === 0) { resolve({ success: true, data: output.trim(), }); } else { resolve({ success: false, error: errorOutput.trim(), }); } }); }); }); }