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.

455 lines (454 loc) 17.6 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.appRouter = void 0; const server_1 = require("@trpc/server"); const zod_1 = require("zod"); 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 Message_1 = require("../dao/Message"); const Reply_1 = require("../dao/Reply"); const Run_1 = require("../dao/Run"); const Trace_1 = require("../dao/Trace"); const socket_1 = require("./socket"); const src_2 = require("../../../shared/src"); const server_2 = require("../../../shared/src/config/server"); const textBlock = zod_1.z.object({ text: zod_1.z.string(), type: zod_1.z.literal(src_1.BlockType.TEXT), }); const thinkingBlock = zod_1.z.object({ thinking: zod_1.z.string(), type: zod_1.z.literal(src_1.BlockType.THINKING), }); const base64Source = zod_1.z.object({ type: zod_1.z.literal('base64'), media_type: zod_1.z.string(), data: zod_1.z.string(), }); const urlSource = zod_1.z.object({ type: zod_1.z.literal('url'), url: zod_1.z.string(), }); const imageBlock = zod_1.z.object({ type: zod_1.z.literal(src_1.BlockType.IMAGE), source: zod_1.z.union([base64Source, urlSource]), }); const audioBlock = zod_1.z.object({ type: zod_1.z.literal(src_1.BlockType.AUDIO), source: zod_1.z.union([base64Source, urlSource]), }); const videoBlock = zod_1.z.object({ type: zod_1.z.literal(src_1.BlockType.VIDEO), source: zod_1.z.union([base64Source, urlSource]), }); const toolUseBlock = zod_1.z.object({ type: zod_1.z.literal(src_1.BlockType.TOOL_USE), id: zod_1.z.string(), name: zod_1.z.string(), input: zod_1.z.record(zod_1.z.unknown()), }); const toolResultBlock = zod_1.z.object({ type: zod_1.z.literal(src_1.BlockType.TOOL_RESULT), id: zod_1.z.string(), name: zod_1.z.string(), output: zod_1.z.union([ zod_1.z.string(), zod_1.z.array(zod_1.z.union([textBlock, imageBlock, audioBlock, videoBlock])), ]), }); // Define ContentBlock as a union of all possible block types const contentBlock = zod_1.z.union([ textBlock, thinkingBlock, imageBlock, audioBlock, videoBlock, toolUseBlock, toolResultBlock, ]); // Define ContentBlocks as an array of ContentBlock const contentBlocks = zod_1.z.array(contentBlock); // Define ContentType as a string or ContentBlocks const contentType = zod_1.z.union([zod_1.z.string(), contentBlocks]); const t = server_1.initTRPC.create(); exports.appRouter = t.router({ registerRun: t.procedure .input(zod_1.z.object({ id: zod_1.z.string(), project: zod_1.z.string(), name: zod_1.z.string(), timestamp: zod_1.z.string(), pid: zod_1.z.number(), status: zod_1.z.enum(Object.values(src_1.Status)), // Deprecated run_dir: zod_1.z.string().optional().nullable(), })) .mutation((_a) => __awaiter(void 0, [_a], void 0, function* ({ input }) { const runData = { id: input.id, project: input.project, name: input.name, timestamp: input.timestamp, run_dir: input.run_dir || '', // Deprecated pid: input.pid, status: input.status, }; yield Run_1.RunDao.addRun(runData); // Notify the subscribers of the specific project socket_1.SocketManager.broadcastRunToProjectRoom(input.project); // Notify the clients of the project list socket_1.SocketManager.broadcastRunToProjectListRoom(); // Notify the clients of the overview room socket_1.SocketManager.broadcastOverviewDataToDashboardRoom(); })), requestUserInput: t.procedure .input(zod_1.z.object({ requestId: zod_1.z.string(), runId: zod_1.z.string(), agentId: zod_1.z.string(), agentName: zod_1.z.string(), structuredInput: zod_1.z.record(zod_1.z.unknown()).nullable(), })) .mutation((_a) => __awaiter(void 0, [_a], void 0, function* ({ input }) { const runExist = yield Run_1.RunDao.doesRunExist(input.runId); if (!runExist) { throw new server_1.TRPCError({ code: 'BAD_REQUEST', message: `Run with id ${input.runId} does not exist`, }); } try { // Save the input request to the database yield InputRequest_1.InputRequestDao.saveInputRequest({ requestId: input.requestId, runId: input.runId, agentId: input.agentId, agentName: input.agentName, structuredInput: input.structuredInput, }); console.debug(`${input.runId}: input request saved with id ${input.requestId}`); // Broadcast the input request to the run room socket_1.SocketManager.broadcastInputRequestToRunRoom(input.runId, { requestId: input.requestId, agentId: input.agentId, agentName: input.agentName, structuredInput: input.structuredInput, }); } catch (error) { console.error(error); throw new server_1.TRPCError({ code: 'BAD_REQUEST', message: 'Failed to save input request, look at the server logs for more information', }); } })), registerReply: t.procedure .input(src_1.RegisterReplyParamsSchema) .mutation((_a) => __awaiter(void 0, [_a], void 0, function* ({ input }) { try { yield Reply_1.ReplyDao.saveReply(input); } catch (error) { console.error(error); throw new server_1.TRPCError({ code: 'BAD_REQUEST', message: `Failed to register reply for error: ${error}`, }); } })), pushMessage: t.procedure .input(zod_1.z.object({ runId: zod_1.z.string(), replyId: zod_1.z.string().optional().nullable(), replyName: zod_1.z.string().optional().nullable(), replyRole: zod_1.z.string().optional().nullable(), msg: zod_1.z.object({ id: zod_1.z.string(), name: zod_1.z.string(), role: zod_1.z.string(), content: contentType, metadata: zod_1.z.unknown(), timestamp: zod_1.z.string(), }), // The name and role here are deprecated, use replyName and replyRole instead name: zod_1.z.string().optional().nullable(), role: zod_1.z.string().optional().nullable(), // Speech audio data for real-time playback speech: zod_1.z .union([audioBlock, zod_1.z.array(audioBlock)]) .optional() .nullable(), })) .mutation((_a) => __awaiter(void 0, [_a], void 0, function* ({ input }) { var _b, _c, _d; const runExist = yield Run_1.RunDao.doesRunExist(input.runId); console.log('Received pushMessage:', input); if (!runExist) { throw new server_1.TRPCError({ code: 'BAD_REQUEST', message: `Run with id ${input.runId} does not exist`, }); } // Let's determine the replyId to use for this message const replyId = (_b = input.replyId) !== null && _b !== void 0 ? _b : input.msg.id; // Check if the replyId exists if (!(yield Reply_1.ReplyDao.doesReplyExist(replyId))) { // Create a reply record if it does not exist yield Reply_1.ReplyDao.saveReply({ runId: input.runId, replyId: replyId, replyRole: (_c = input.replyRole) !== null && _c !== void 0 ? _c : input.role, replyName: (_d = input.replyName) !== null && _d !== void 0 ? _d : input.name, createdAt: input.msg.timestamp, }); } // Normalize speech to array if provided let speechArray = null; if (input.speech) { speechArray = Array.isArray(input.speech) ? input.speech : [input.speech]; } // Save the message to the database const msgFormData = { id: input.msg.id, runId: input.runId, replyId: replyId, msg: { name: input.msg.name, role: input.msg.role, content: input.msg.content, metadata: input.msg.metadata, timestamp: input.msg.timestamp, }, speech: speechArray, }; // Save the message yield Message_1.MessageDao.saveMessage(msgFormData); console.debug(`RUN-${input.runId}: message saved`); // Obtain the reply and broadcast to the frontend Reply_1.ReplyDao.getReply(replyId) .then((reply) => { // Broadcast the message to the run room console.debug(`Broadcasting message to room run-${input.runId}`); if (reply) { socket_1.SocketManager.broadcastMessageToRunRoom(input.runId, reply); } else { console.error(`Reply with id ${replyId} not found for broadcasting`); } }) .catch((error) => { console.error(error); throw error; }); // Broadcast speech data if provided (for real-time audio playback) if (input.speech !== null && input.speech !== undefined) { // Type assertion needed because Zod infers literal strings // while AudioBlock uses SourceType enum socket_1.SocketManager.broadcastSpeechToRunRoom(input.runId, replyId, input.speech); } })), pushMessageToFridayApp: t.procedure .input(zod_1.z.object({ replyId: zod_1.z.string(), msg: zod_1.z.object({ id: zod_1.z.string(), name: zod_1.z.string(), role: zod_1.z.string(), content: contentBlocks, metadata: zod_1.z.unknown(), timestamp: zod_1.z.string(), }), })) .mutation((_a) => __awaiter(void 0, [_a], void 0, function* ({ input }) { try { const reply = yield FridayAppMessage_1.FridayAppMessageDao.saveReplyMessage(input.replyId, input.msg, false); // Broadcast to all the clients in the FridayAppRoom socket_1.SocketManager.broadcastReplyToFridayAppRoom(reply); } catch (error) { console.error(error); throw error; } })), pushFinishedSignalToFridayApp: t.procedure .input(zod_1.z.object({ replyId: zod_1.z.string(), })) .mutation((_a) => __awaiter(void 0, [_a], void 0, function* ({ input }) { FridayAppMessage_1.FridayAppMessageDao.finishReply(input.replyId) .then((reply) => { // Broadcast to all the clients in the FridayAppRoom socket_1.SocketManager.broadcastReplyToFridayAppRoom(reply); }) .catch((error) => { console.error(error); throw error; }); })), clientGetFridayConfig: t.procedure.query(() => __awaiter(void 0, void 0, void 0, function* () { return friday_1.FridayConfigManager.getInstance().getConfig(); })), /** * Get paginated projects with optional sorting and filtering * * @param pagination - Pagination parameters (page number and page size) * @param sort - Optional sorting configuration (field name and order) * @param filters - Optional filters for project search (e.g., project name) * @returns ResponseBody containing TableData with project list and metadata * * @example * Input: { * pagination: { page: 1, pageSize: 10 }, * sort: { field: 'createdAt', order: 'desc' }, * filters: { project: 'my-project' } * } * * Output: { * success: true, * message: 'Projects fetched successfully', * data: { * list: [...], * total: 100, * page: 1, * pageSize: 10 * } * } */ getProjects: t.procedure .input(src_1.TableRequestParamsSchema) .query((_a) => __awaiter(void 0, [_a], void 0, function* ({ input }) { try { console.debug('[TRPC] getProjects called with input:', input); const result = yield Run_1.RunDao.getProjects(input); return { success: true, message: 'Projects fetched successfully', data: result, }; } catch (error) { console.error('Error in getProjects:', error); throw new server_1.TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: error instanceof Error ? error.message : 'Failed to get projects', }); } })), getTraces: t.procedure .input(src_1.TableRequestParamsSchema) .query((_a) => __awaiter(void 0, [_a], void 0, function* ({ input }) { try { console.debug('[TRPC] getTraces called with input:', input); const result = yield Trace_1.SpanDao.getTraces(input); return { success: true, message: 'Traces fetched successfully', data: result, }; } catch (error) { console.error('Error in getTraces:', error); throw new server_1.TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: error instanceof Error ? error.message : 'Failed to get trace list', }); } })), getTrace: t.procedure .input(src_1.GetTraceParamsSchema) .query((_a) => __awaiter(void 0, [_a], void 0, function* ({ input }) { try { return yield Trace_1.SpanDao.getTrace(input.traceId); } catch (error) { console.error('Error in getTrace:', error); throw new server_1.TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: error instanceof Error ? error.message : 'Failed to get trace', }); } })), getTraceStatistic: t.procedure .input(src_1.GetTraceStatisticParamsSchema) .query((_a) => __awaiter(void 0, [_a], void 0, function* ({ input }) { try { return yield Trace_1.SpanDao.getTraceStatistic(input); } catch (error) { console.error('Error in getTraceStatistic:', error); throw new server_1.TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: error instanceof Error ? error.message : 'Failed to get trace statistics', }); } })), getCurrentVersion: t.procedure.query(() => __awaiter(void 0, void 0, void 0, function* () { try { const version = src_2.APP_INFO.version; return { success: true, message: 'Version retrieved successfully', data: { version: version, }, }; } catch (error) { console.error('Error get current version:', error); throw new server_1.TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: error instanceof Error ? error.message : 'Failed to get current version', }); } })), getDataInfo: t.procedure.query(() => __awaiter(void 0, void 0, void 0, function* () { try { const configManager = server_2.ConfigManager.getInstance(); const dbStats = configManager.getDataStats(); return { success: true, message: 'Database info retrieved successfully', data: { path: dbStats.path, size: dbStats.size, formattedSize: dbStats.formattedSize, fridayConfigPath: dbStats.fridayConfigPath, fridayHistoryPath: dbStats.fridayHistoryPath, }, }; } catch (error) { console.error('Error get database info:', error); throw new server_1.TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: error instanceof Error ? error.message : 'Failed to get database info', }); } })), });