mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
feat: Implement logging for AI Agent task execution lifecycle
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { ONEUPTIME_URL } from "../Config";
|
||||
import AIAgentAPIRequest from "../Utils/AIAgentAPIRequest";
|
||||
import AIAgentTaskLog from "../Utils/AIAgentTaskLog";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import API from "Common/Utils/API";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
@@ -86,6 +87,9 @@ const startTaskProcessingLoop: () => Promise<void> =
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Send task started log */
|
||||
await AIAgentTaskLog.sendTaskStartedLog(taskId);
|
||||
|
||||
/* Execute the task (empty function for now) */
|
||||
await executeTask(task);
|
||||
|
||||
@@ -102,6 +106,8 @@ const startTaskProcessingLoop: () => Promise<void> =
|
||||
if (!completedResult.isSuccess()) {
|
||||
logger.error(`Failed to mark task ${taskId} as Completed`);
|
||||
} else {
|
||||
/* Send task completed log */
|
||||
await AIAgentTaskLog.sendTaskCompletedLog(taskId);
|
||||
logger.info(`Task completed successfully: ${taskId}`);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -125,6 +131,9 @@ const startTaskProcessingLoop: () => Promise<void> =
|
||||
);
|
||||
}
|
||||
|
||||
/* Send task error log */
|
||||
await AIAgentTaskLog.sendTaskErrorLog(taskId, errorMessage);
|
||||
|
||||
logger.error(`Task failed: ${taskId} - ${errorMessage}`);
|
||||
logger.error(error);
|
||||
}
|
||||
|
||||
79
AIAgent/Utils/AIAgentTaskLog.ts
Normal file
79
AIAgent/Utils/AIAgentTaskLog.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { ONEUPTIME_URL } from "../Config";
|
||||
import AIAgentAPIRequest from "./AIAgentAPIRequest";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import API from "Common/Utils/API";
|
||||
import HTTPResponse from "Common/Types/API/HTTPResponse";
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import LogSeverity from "Common/Types/Log/LogSeverity";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
|
||||
export interface SendLogOptions {
|
||||
taskId: string;
|
||||
severity: LogSeverity;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export default class AIAgentTaskLog {
|
||||
private static createLogUrl: URL | null = null;
|
||||
|
||||
private static getCreateLogUrl(): URL {
|
||||
if (!this.createLogUrl) {
|
||||
this.createLogUrl = URL.fromString(ONEUPTIME_URL.toString()).addRoute(
|
||||
"/api/ai-agent-task-log/create-log",
|
||||
);
|
||||
}
|
||||
return this.createLogUrl;
|
||||
}
|
||||
|
||||
public static async sendLog(options: SendLogOptions): Promise<boolean> {
|
||||
try {
|
||||
const result: HTTPResponse<JSONObject> = await API.post({
|
||||
url: this.getCreateLogUrl(),
|
||||
data: {
|
||||
...AIAgentAPIRequest.getDefaultRequestBody(),
|
||||
taskId: options.taskId,
|
||||
severity: options.severity,
|
||||
message: options.message,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result.isSuccess()) {
|
||||
logger.error(`Failed to send log for task ${options.taskId}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error(`Error sending log for task ${options.taskId}:`);
|
||||
logger.error(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static async sendTaskStartedLog(taskId: string): Promise<boolean> {
|
||||
return this.sendLog({
|
||||
taskId,
|
||||
severity: LogSeverity.Information,
|
||||
message: "Task execution started",
|
||||
});
|
||||
}
|
||||
|
||||
public static async sendTaskCompletedLog(taskId: string): Promise<boolean> {
|
||||
return this.sendLog({
|
||||
taskId,
|
||||
severity: LogSeverity.Information,
|
||||
message: "Task execution completed successfully",
|
||||
});
|
||||
}
|
||||
|
||||
public static async sendTaskErrorLog(
|
||||
taskId: string,
|
||||
errorMessage: string,
|
||||
): Promise<boolean> {
|
||||
return this.sendLog({
|
||||
taskId,
|
||||
severity: LogSeverity.Error,
|
||||
message: `Task execution failed: ${errorMessage}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,22 @@
|
||||
import AIAgentTaskLogService, {
|
||||
Service as AIAgentTaskLogServiceType,
|
||||
} from "../Services/AIAgentTaskLogService";
|
||||
import AIAgentService from "../Services/AIAgentService";
|
||||
import AIAgentTaskService from "../Services/AIAgentTaskService";
|
||||
import {
|
||||
ExpressRequest,
|
||||
ExpressResponse,
|
||||
NextFunction,
|
||||
} from "../Utils/Express";
|
||||
import Response from "../Utils/Response";
|
||||
import BaseAPI from "./BaseAPI";
|
||||
import AIAgentTaskLog from "../../Models/DatabaseModels/AIAgentTaskLog";
|
||||
import AIAgent from "../../Models/DatabaseModels/AIAgent";
|
||||
import AIAgentTask from "../../Models/DatabaseModels/AIAgentTask";
|
||||
import BadDataException from "../../Types/Exception/BadDataException";
|
||||
import { JSONObject } from "../../Types/JSON";
|
||||
import ObjectID from "../../Types/ObjectID";
|
||||
import LogSeverity from "../../Types/Log/LogSeverity";
|
||||
|
||||
export default class AIAgentTaskLogAPI extends BaseAPI<
|
||||
AIAgentTaskLog,
|
||||
@@ -10,5 +24,141 @@ export default class AIAgentTaskLogAPI extends BaseAPI<
|
||||
> {
|
||||
public constructor() {
|
||||
super(AIAgentTaskLog, AIAgentTaskLogService);
|
||||
|
||||
/*
|
||||
* Create a log entry for an AI Agent task
|
||||
* Validates aiAgentId and aiAgentKey before creating log
|
||||
*/
|
||||
this.router.post(
|
||||
`${new this.entityType().getCrudApiPath()?.toString()}/create-log`,
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
try {
|
||||
const data: JSONObject = req.body;
|
||||
|
||||
/* Validate AI Agent credentials */
|
||||
const aiAgent: AIAgent | null = await this.validateAIAgent(data);
|
||||
|
||||
if (!aiAgent) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException("Invalid AI Agent ID or AI Agent Key"),
|
||||
);
|
||||
}
|
||||
|
||||
/* Validate required fields */
|
||||
if (!data["taskId"]) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException("taskId is required"),
|
||||
);
|
||||
}
|
||||
|
||||
if (!data["severity"]) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException("severity is required"),
|
||||
);
|
||||
}
|
||||
|
||||
if (!data["message"]) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException("message is required"),
|
||||
);
|
||||
}
|
||||
|
||||
const taskId: ObjectID = new ObjectID(data["taskId"] as string);
|
||||
const severity: LogSeverity = data["severity"] as LogSeverity;
|
||||
const message: string = data["message"] as string;
|
||||
|
||||
/* Validate severity value */
|
||||
const validSeverities: Array<LogSeverity> = Object.values(LogSeverity);
|
||||
if (!validSeverities.includes(severity)) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException(
|
||||
`Invalid severity. Must be one of: ${validSeverities.join(", ")}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/* Check if task exists and get project ID */
|
||||
const existingTask: AIAgentTask | null =
|
||||
await AIAgentTaskService.findOneById({
|
||||
id: taskId,
|
||||
select: {
|
||||
_id: true,
|
||||
projectId: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingTask) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException("Task not found"),
|
||||
);
|
||||
}
|
||||
|
||||
/* Create the log entry */
|
||||
const logEntry: AIAgentTaskLog = new AIAgentTaskLog();
|
||||
logEntry.projectId = existingTask.projectId!;
|
||||
logEntry.aiAgentTaskId = taskId;
|
||||
logEntry.aiAgentId = aiAgent.id!;
|
||||
logEntry.severity = severity;
|
||||
logEntry.message = message;
|
||||
|
||||
await AIAgentTaskLogService.create({
|
||||
data: logEntry,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
return Response.sendJsonObjectResponse(req, res, {
|
||||
taskId: taskId.toString(),
|
||||
message: "Log entry created successfully",
|
||||
});
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate AI Agent credentials from request body
|
||||
* Returns AIAgent if valid, null otherwise
|
||||
*/
|
||||
private async validateAIAgent(data: JSONObject): Promise<AIAgent | null> {
|
||||
if (!data["aiAgentId"] || !data["aiAgentKey"]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const aiAgentId: ObjectID = new ObjectID(data["aiAgentId"] as string);
|
||||
const aiAgentKey: string = data["aiAgentKey"] as string;
|
||||
|
||||
const aiAgent: AIAgent | null = await AIAgentService.findOneBy({
|
||||
query: {
|
||||
_id: aiAgentId.toString(),
|
||||
key: aiAgentKey,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
return aiAgent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ const AIAgentTaskViewLayout: FunctionComponent<
|
||||
title="AI Agent Task"
|
||||
modelType={AIAgentTask}
|
||||
modelId={modelId}
|
||||
modelNameField="_id"
|
||||
modelNameField="name"
|
||||
breadcrumbLinks={getAIAgentTasksBreadcrumbs(path)}
|
||||
sideMenu={<SideMenu modelId={modelId} />}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user