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