@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
JavaScript
;
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(),
});
}
});
});
});
}