Files
oneuptime/Common/Server/Services/TelemetryExceptionService.ts

172 lines
5.3 KiB
TypeScript

import DatabaseService from "./DatabaseService";
import Model from "../../Models/DatabaseModels/TelemetryException";
import AIAgentTask from "../../Models/DatabaseModels/AIAgentTask";
import AIAgentTaskTelemetryException from "../../Models/DatabaseModels/AIAgentTaskTelemetryException";
import ObjectID from "../../Types/ObjectID";
import BadDataException from "../../Types/Exception/BadDataException";
import AIAgentTaskType from "../../Types/AI/AIAgentTaskType";
import AIAgentTaskStatus from "../../Types/AI/AIAgentTaskStatus";
import { FixExceptionTaskMetadata } from "../../Types/AI/AIAgentTaskMetadata";
import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
import AIAgentTaskService from "./AIAgentTaskService";
import AIAgentTaskTelemetryExceptionService from "./AIAgentTaskTelemetryExceptionService";
import QueryHelper from "../Types/Database/QueryHelper";
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
export interface CreateAIAgentTaskForExceptionParams {
telemetryExceptionId: ObjectID;
props: DatabaseCommonInteractionProps;
}
export class Service extends DatabaseService<Model> {
public constructor() {
super(Model);
}
@CaptureSpan()
public async createAIAgentTaskForException(
params: CreateAIAgentTaskForExceptionParams,
): Promise<AIAgentTask> {
const { telemetryExceptionId, props } = params;
// Get the telemetry exception
const telemetryException: Model | null = await this.findOneById({
id: telemetryExceptionId,
select: {
_id: true,
projectId: true,
message: true,
stackTrace: true,
serviceId: true,
exceptionType: true,
},
props,
});
if (!telemetryException || !telemetryException.projectId) {
throw new BadDataException("Telemetry Exception not found");
}
// Check if an active AI agent task already exists for this exception
await this.validateNoActiveTaskExists(telemetryExceptionId);
// Create the AI Agent Task
const createdTask: AIAgentTask = await this.createFixExceptionTask({
telemetryException,
telemetryExceptionId,
props,
});
// Link the task to the telemetry exception
await AIAgentTaskTelemetryExceptionService.linkTaskToTelemetryException({
projectId: telemetryException.projectId,
aiAgentTaskId: createdTask.id!,
telemetryExceptionId,
props,
});
return createdTask;
}
@CaptureSpan()
private async validateNoActiveTaskExists(
telemetryExceptionId: ObjectID,
): Promise<void> {
const existingTaskLink: AIAgentTaskTelemetryException | null =
await AIAgentTaskTelemetryExceptionService.findOneBy({
query: {
telemetryExceptionId: telemetryExceptionId,
aiAgentTask: {
status: QueryHelper.notIn([
AIAgentTaskStatus.Completed,
AIAgentTaskStatus.Error,
]),
},
},
select: {
_id: true,
aiAgentTaskId: true,
},
props: {
isRoot: true,
},
});
if (existingTaskLink) {
throw new BadDataException(
"An AI agent task is already in progress for this exception. Please wait for it to complete before creating a new one.",
);
}
}
@CaptureSpan()
private async createFixExceptionTask(params: {
telemetryException: Model;
telemetryExceptionId: ObjectID;
props: DatabaseCommonInteractionProps;
}): Promise<AIAgentTask> {
const { telemetryException, telemetryExceptionId, props } = params;
const aiAgentTask: AIAgentTask = new AIAgentTask();
aiAgentTask.projectId = telemetryException.projectId!;
aiAgentTask.taskType = AIAgentTaskType.FixException;
aiAgentTask.status = AIAgentTaskStatus.Scheduled;
// Set name and description based on exception details
const exceptionType: string =
telemetryException.exceptionType || "Exception";
const exceptionMessage: string =
telemetryException.message || "No message available";
aiAgentTask.name = `Fix ${exceptionType}: ${exceptionMessage}`;
aiAgentTask.description = `AI Agent task to fix the exception: ${exceptionMessage}`;
// Build metadata
aiAgentTask.metadata = this.buildFixExceptionMetadata({
telemetryException,
telemetryExceptionId,
});
const createdTask: AIAgentTask = await AIAgentTaskService.create({
data: aiAgentTask,
props: {
...props,
},
});
if (!createdTask.id) {
throw new BadDataException("Failed to create AI Agent Task");
}
return createdTask;
}
private buildFixExceptionMetadata(params: {
telemetryException: Model;
telemetryExceptionId: ObjectID;
}): FixExceptionTaskMetadata {
const { telemetryException, telemetryExceptionId } = params;
const metadata: FixExceptionTaskMetadata = {
taskType: AIAgentTaskType.FixException,
exceptionId: telemetryExceptionId.toString(),
};
if (telemetryException.stackTrace) {
metadata.stackTrace = telemetryException.stackTrace;
}
if (telemetryException.message) {
metadata.errorMessage = telemetryException.message;
}
if (telemetryException.serviceId) {
metadata.serviceId = telemetryException.serviceId.toString();
}
return metadata;
}
}
export default new Service();