@oneuptime/common 9.2.17 → 9.2.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/Server/API/AlertAPI.ts +139 -0
  2. package/Server/API/IncidentAPI.ts +132 -0
  3. package/Server/API/ScheduledMaintenanceAPI.ts +164 -0
  4. package/Server/Services/AIService.ts +0 -1
  5. package/Server/Services/IncidentService.ts +0 -1
  6. package/Server/Utils/AI/AlertAIContextBuilder.ts +264 -0
  7. package/Server/Utils/AI/IncidentAIContextBuilder.ts +212 -0
  8. package/Server/Utils/AI/ScheduledMaintenanceAIContextBuilder.ts +345 -0
  9. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +78 -1
  10. package/Server/Utils/Workspace/Slack/Slack.ts +80 -1
  11. package/Tests/Types/Domain.test.ts +24 -3
  12. package/Types/Domain.ts +21 -24
  13. package/UI/Components/AI/GenerateFromAIModal.tsx +157 -20
  14. package/build/dist/Server/API/AlertAPI.js +94 -0
  15. package/build/dist/Server/API/AlertAPI.js.map +1 -0
  16. package/build/dist/Server/API/IncidentAPI.js +88 -1
  17. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  18. package/build/dist/Server/API/ScheduledMaintenanceAPI.js +103 -0
  19. package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -0
  20. package/build/dist/Server/Services/AIService.js +0 -1
  21. package/build/dist/Server/Services/AIService.js.map +1 -1
  22. package/build/dist/Server/Services/IncidentService.js +0 -1
  23. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  24. package/build/dist/Server/Utils/AI/AlertAIContextBuilder.js +238 -0
  25. package/build/dist/Server/Utils/AI/AlertAIContextBuilder.js.map +1 -0
  26. package/build/dist/Server/Utils/AI/IncidentAIContextBuilder.js +189 -0
  27. package/build/dist/Server/Utils/AI/IncidentAIContextBuilder.js.map +1 -1
  28. package/build/dist/Server/Utils/AI/ScheduledMaintenanceAIContextBuilder.js +311 -0
  29. package/build/dist/Server/Utils/AI/ScheduledMaintenanceAIContextBuilder.js.map +1 -0
  30. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +59 -2
  31. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  32. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +61 -1
  33. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  34. package/build/dist/Tests/Types/Domain.test.js +19 -3
  35. package/build/dist/Tests/Types/Domain.test.js.map +1 -1
  36. package/build/dist/Types/Domain.js +18 -16
  37. package/build/dist/Types/Domain.js.map +1 -1
  38. package/build/dist/UI/Components/AI/GenerateFromAIModal.js +116 -3
  39. package/build/dist/UI/Components/AI/GenerateFromAIModal.js.map +1 -1
  40. package/package.json +1 -1
@@ -0,0 +1,139 @@
1
+ import Alert from "../../Models/DatabaseModels/Alert";
2
+ import NotFoundException from "../../Types/Exception/NotFoundException";
3
+ import BadDataException from "../../Types/Exception/BadDataException";
4
+ import ObjectID from "../../Types/ObjectID";
5
+ import AlertService, {
6
+ Service as AlertServiceType,
7
+ } from "../Services/AlertService";
8
+ import UserMiddleware from "../Middleware/UserAuthorization";
9
+ import Response from "../Utils/Response";
10
+ import BaseAPI from "./BaseAPI";
11
+ import {
12
+ ExpressRequest,
13
+ ExpressResponse,
14
+ NextFunction,
15
+ } from "../Utils/Express";
16
+ import CommonAPI from "./CommonAPI";
17
+ import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
18
+ import AIService, { AILogRequest, AILogResponse } from "../Services/AIService";
19
+ import AlertAIContextBuilder, {
20
+ AIGenerationContext,
21
+ AlertContextData,
22
+ } from "../Utils/AI/AlertAIContextBuilder";
23
+ import JSONFunctions from "../../Types/JSONFunctions";
24
+ import Permission from "../../Types/Permission";
25
+
26
+ export default class AlertAPI extends BaseAPI<Alert, AlertServiceType> {
27
+ public constructor() {
28
+ super(Alert, AlertService);
29
+
30
+ // Generate note from AI
31
+ this.router.post(
32
+ `${new this.entityType().getCrudApiPath()?.toString()}/generate-note-from-ai/:alertId`,
33
+ UserMiddleware.getUserMiddleware,
34
+ async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
35
+ try {
36
+ await this.generateNoteFromAI(req, res);
37
+ } catch (err) {
38
+ next(err);
39
+ }
40
+ },
41
+ );
42
+ }
43
+
44
+ private async generateNoteFromAI(
45
+ req: ExpressRequest,
46
+ res: ExpressResponse,
47
+ ): Promise<void> {
48
+ const alertIdParam: string | undefined = req.params["alertId"];
49
+
50
+ if (!alertIdParam) {
51
+ throw new BadDataException("Alert ID is required");
52
+ }
53
+
54
+ let alertId: ObjectID;
55
+
56
+ try {
57
+ alertId = new ObjectID(alertIdParam);
58
+ } catch {
59
+ throw new BadDataException("Invalid Alert ID");
60
+ }
61
+
62
+ const props: DatabaseCommonInteractionProps =
63
+ await CommonAPI.getDatabaseCommonInteractionProps(req);
64
+
65
+ // Verify user has permission
66
+ const permissions: Array<Permission> | undefined = props
67
+ .userTenantAccessPermission?.["permissions"] as
68
+ | Array<Permission>
69
+ | undefined;
70
+
71
+ const hasPermission: boolean = permissions
72
+ ? permissions.some((p: Permission) => {
73
+ return (
74
+ p === Permission.ProjectOwner ||
75
+ p === Permission.ProjectAdmin ||
76
+ p === Permission.EditAlert ||
77
+ p === Permission.CreateAlertInternalNote
78
+ );
79
+ })
80
+ : false;
81
+
82
+ if (!hasPermission && !props.isMasterAdmin) {
83
+ throw new BadDataException(
84
+ "You do not have permission to generate notes for this alert.",
85
+ );
86
+ }
87
+
88
+ // Get the template from request body
89
+ const template: string | undefined = JSONFunctions.getJSONValueInPath(
90
+ req.body,
91
+ "template",
92
+ ) as string | undefined;
93
+
94
+ // Get the alert to verify it exists and get the project ID
95
+ const alert: Alert | null = await this.service.findOneById({
96
+ id: alertId,
97
+ select: {
98
+ _id: true,
99
+ projectId: true,
100
+ },
101
+ props,
102
+ });
103
+
104
+ if (!alert || !alert.projectId) {
105
+ throw new NotFoundException("Alert not found");
106
+ }
107
+
108
+ // Build alert context
109
+ const contextData: AlertContextData =
110
+ await AlertAIContextBuilder.buildAlertContext({
111
+ alertId,
112
+ });
113
+
114
+ // Format context for note generation
115
+ const aiContext: AIGenerationContext =
116
+ AlertAIContextBuilder.formatAlertContextForNote(contextData, template);
117
+
118
+ // Generate note using AIService (handles billing and logging)
119
+ const aiLogRequest: AILogRequest = {
120
+ projectId: alert.projectId,
121
+ feature: "Alert Internal Note",
122
+ alertId: alertId,
123
+ messages: aiContext.messages,
124
+ maxTokens: 4096,
125
+ temperature: 0.7,
126
+ };
127
+
128
+ if (props.userId) {
129
+ aiLogRequest.userId = props.userId;
130
+ }
131
+
132
+ const response: AILogResponse =
133
+ await AIService.executeWithLogging(aiLogRequest);
134
+
135
+ return Response.sendJsonObjectResponse(req, res, {
136
+ note: response.content,
137
+ });
138
+ }
139
+ }
@@ -59,6 +59,21 @@ export default class IncidentAPI extends BaseAPI<
59
59
  }
60
60
  },
61
61
  );
62
+
63
+ // Generate note from AI
64
+ this.router.post(
65
+ `${new this.entityType()
66
+ .getCrudApiPath()
67
+ ?.toString()}/generate-note-from-ai/:incidentId`,
68
+ UserMiddleware.getUserMiddleware,
69
+ async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
70
+ try {
71
+ await this.generateNoteFromAI(req, res);
72
+ } catch (err) {
73
+ next(err);
74
+ }
75
+ },
76
+ );
62
77
  }
63
78
 
64
79
  private async getPostmortemAttachment(
@@ -229,4 +244,121 @@ export default class IncidentAPI extends BaseAPI<
229
244
  postmortemNote: response.content,
230
245
  });
231
246
  }
247
+
248
+ private async generateNoteFromAI(
249
+ req: ExpressRequest,
250
+ res: ExpressResponse,
251
+ ): Promise<void> {
252
+ const incidentIdParam: string | undefined = req.params["incidentId"];
253
+
254
+ if (!incidentIdParam) {
255
+ throw new BadDataException("Incident ID is required");
256
+ }
257
+
258
+ let incidentId: ObjectID;
259
+
260
+ try {
261
+ incidentId = new ObjectID(incidentIdParam);
262
+ } catch {
263
+ throw new BadDataException("Invalid Incident ID");
264
+ }
265
+
266
+ const props: DatabaseCommonInteractionProps =
267
+ await CommonAPI.getDatabaseCommonInteractionProps(req);
268
+
269
+ // Verify user has permission to edit the incident
270
+ const permissions: Array<Permission> | undefined = props
271
+ .userTenantAccessPermission?.["permissions"] as
272
+ | Array<Permission>
273
+ | undefined;
274
+
275
+ const hasPermission: boolean = permissions
276
+ ? permissions.some((p: Permission) => {
277
+ return (
278
+ p === Permission.ProjectOwner ||
279
+ p === Permission.ProjectAdmin ||
280
+ p === Permission.EditProjectIncident ||
281
+ p === Permission.CreateIncidentInternalNote ||
282
+ p === Permission.CreateIncidentPublicNote
283
+ );
284
+ })
285
+ : false;
286
+
287
+ if (!hasPermission && !props.isMasterAdmin) {
288
+ throw new BadDataException(
289
+ "You do not have permission to generate notes for this incident.",
290
+ );
291
+ }
292
+
293
+ // Get the template and note type from request body
294
+ const template: string | undefined = JSONFunctions.getJSONValueInPath(
295
+ req.body,
296
+ "template",
297
+ ) as string | undefined;
298
+
299
+ const noteType: string =
300
+ (JSONFunctions.getJSONValueInPath(req.body, "noteType") as string) ||
301
+ "internal";
302
+
303
+ if (noteType !== "public" && noteType !== "internal") {
304
+ throw new BadDataException("Note type must be 'public' or 'internal'");
305
+ }
306
+
307
+ // Always include workspace messages for comprehensive context
308
+ const includeWorkspaceMessages: boolean = true;
309
+
310
+ // Get the incident to verify it exists and get the project ID
311
+ const incident: Incident | null = await this.service.findOneById({
312
+ id: incidentId,
313
+ select: {
314
+ _id: true,
315
+ projectId: true,
316
+ },
317
+ props,
318
+ });
319
+
320
+ if (!incident || !incident.projectId) {
321
+ throw new NotFoundException("Incident not found");
322
+ }
323
+
324
+ // Build incident context
325
+ const contextData: IncidentContextData =
326
+ await IncidentAIContextBuilder.buildIncidentContext({
327
+ incidentId,
328
+ includeWorkspaceMessages,
329
+ workspaceMessageLimit: 300,
330
+ });
331
+
332
+ // Format context for note generation
333
+ const aiContext: AIGenerationContext =
334
+ IncidentAIContextBuilder.formatIncidentContextForNote(
335
+ contextData,
336
+ noteType as "public" | "internal",
337
+ template,
338
+ );
339
+
340
+ // Generate note using AIService (handles billing and logging)
341
+ const aiLogRequest: AILogRequest = {
342
+ projectId: incident.projectId,
343
+ feature:
344
+ noteType === "public"
345
+ ? "Incident Public Note"
346
+ : "Incident Internal Note",
347
+ incidentId: incidentId,
348
+ messages: aiContext.messages,
349
+ maxTokens: 4096,
350
+ temperature: 0.7,
351
+ };
352
+
353
+ if (props.userId) {
354
+ aiLogRequest.userId = props.userId;
355
+ }
356
+
357
+ const response: AILogResponse =
358
+ await AIService.executeWithLogging(aiLogRequest);
359
+
360
+ return Response.sendJsonObjectResponse(req, res, {
361
+ note: response.content,
362
+ });
363
+ }
232
364
  }
@@ -0,0 +1,164 @@
1
+ import ScheduledMaintenance from "../../Models/DatabaseModels/ScheduledMaintenance";
2
+ import NotFoundException from "../../Types/Exception/NotFoundException";
3
+ import BadDataException from "../../Types/Exception/BadDataException";
4
+ import ObjectID from "../../Types/ObjectID";
5
+ import ScheduledMaintenanceService, {
6
+ Service as ScheduledMaintenanceServiceType,
7
+ } from "../Services/ScheduledMaintenanceService";
8
+ import UserMiddleware from "../Middleware/UserAuthorization";
9
+ import Response from "../Utils/Response";
10
+ import BaseAPI from "./BaseAPI";
11
+ import {
12
+ ExpressRequest,
13
+ ExpressResponse,
14
+ NextFunction,
15
+ } from "../Utils/Express";
16
+ import CommonAPI from "./CommonAPI";
17
+ import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
18
+ import AIService, { AILogRequest, AILogResponse } from "../Services/AIService";
19
+ import ScheduledMaintenanceAIContextBuilder, {
20
+ AIGenerationContext,
21
+ ScheduledMaintenanceContextData,
22
+ } from "../Utils/AI/ScheduledMaintenanceAIContextBuilder";
23
+ import JSONFunctions from "../../Types/JSONFunctions";
24
+ import Permission from "../../Types/Permission";
25
+
26
+ export default class ScheduledMaintenanceAPI extends BaseAPI<
27
+ ScheduledMaintenance,
28
+ ScheduledMaintenanceServiceType
29
+ > {
30
+ public constructor() {
31
+ super(ScheduledMaintenance, ScheduledMaintenanceService);
32
+
33
+ // Generate note from AI
34
+ this.router.post(
35
+ `${new this.entityType()
36
+ .getCrudApiPath()
37
+ ?.toString()}/generate-note-from-ai/:scheduledMaintenanceId`,
38
+ UserMiddleware.getUserMiddleware,
39
+ async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
40
+ try {
41
+ await this.generateNoteFromAI(req, res);
42
+ } catch (err) {
43
+ next(err);
44
+ }
45
+ },
46
+ );
47
+ }
48
+
49
+ private async generateNoteFromAI(
50
+ req: ExpressRequest,
51
+ res: ExpressResponse,
52
+ ): Promise<void> {
53
+ const scheduledMaintenanceIdParam: string | undefined =
54
+ req.params["scheduledMaintenanceId"];
55
+
56
+ if (!scheduledMaintenanceIdParam) {
57
+ throw new BadDataException("Scheduled Maintenance ID is required");
58
+ }
59
+
60
+ let scheduledMaintenanceId: ObjectID;
61
+
62
+ try {
63
+ scheduledMaintenanceId = new ObjectID(scheduledMaintenanceIdParam);
64
+ } catch {
65
+ throw new BadDataException("Invalid Scheduled Maintenance ID");
66
+ }
67
+
68
+ const props: DatabaseCommonInteractionProps =
69
+ await CommonAPI.getDatabaseCommonInteractionProps(req);
70
+
71
+ // Verify user has permission
72
+ const permissions: Array<Permission> | undefined = props
73
+ .userTenantAccessPermission?.["permissions"] as
74
+ | Array<Permission>
75
+ | undefined;
76
+
77
+ const hasPermission: boolean = permissions
78
+ ? permissions.some((p: Permission) => {
79
+ return (
80
+ p === Permission.ProjectOwner ||
81
+ p === Permission.ProjectAdmin ||
82
+ p === Permission.EditProjectScheduledMaintenance ||
83
+ p === Permission.CreateScheduledMaintenanceInternalNote ||
84
+ p === Permission.CreateScheduledMaintenancePublicNote
85
+ );
86
+ })
87
+ : false;
88
+
89
+ if (!hasPermission && !props.isMasterAdmin) {
90
+ throw new BadDataException(
91
+ "You do not have permission to generate notes for this scheduled maintenance.",
92
+ );
93
+ }
94
+
95
+ // Get the template and note type from request body
96
+ const template: string | undefined = JSONFunctions.getJSONValueInPath(
97
+ req.body,
98
+ "template",
99
+ ) as string | undefined;
100
+
101
+ const noteType: string =
102
+ (JSONFunctions.getJSONValueInPath(req.body, "noteType") as string) ||
103
+ "internal";
104
+
105
+ if (noteType !== "public" && noteType !== "internal") {
106
+ throw new BadDataException("Note type must be 'public' or 'internal'");
107
+ }
108
+
109
+ // Get the scheduled maintenance to verify it exists and get the project ID
110
+ const scheduledMaintenance: ScheduledMaintenance | null =
111
+ await this.service.findOneById({
112
+ id: scheduledMaintenanceId,
113
+ select: {
114
+ _id: true,
115
+ projectId: true,
116
+ },
117
+ props,
118
+ });
119
+
120
+ if (!scheduledMaintenance || !scheduledMaintenance.projectId) {
121
+ throw new NotFoundException("Scheduled Maintenance not found");
122
+ }
123
+
124
+ // Build scheduled maintenance context
125
+ const contextData: ScheduledMaintenanceContextData =
126
+ await ScheduledMaintenanceAIContextBuilder.buildScheduledMaintenanceContext(
127
+ {
128
+ scheduledMaintenanceId,
129
+ },
130
+ );
131
+
132
+ // Format context for note generation
133
+ const aiContext: AIGenerationContext =
134
+ ScheduledMaintenanceAIContextBuilder.formatScheduledMaintenanceContextForNote(
135
+ contextData,
136
+ noteType as "public" | "internal",
137
+ template,
138
+ );
139
+
140
+ // Generate note using AIService (handles billing and logging)
141
+ const aiLogRequest: AILogRequest = {
142
+ projectId: scheduledMaintenance.projectId,
143
+ feature:
144
+ noteType === "public"
145
+ ? "Scheduled Maintenance Public Note"
146
+ : "Scheduled Maintenance Internal Note",
147
+ scheduledMaintenanceId: scheduledMaintenanceId,
148
+ messages: aiContext.messages,
149
+ maxTokens: 4096,
150
+ temperature: 0.7,
151
+ };
152
+
153
+ if (props.userId) {
154
+ aiLogRequest.userId = props.userId;
155
+ }
156
+
157
+ const response: AILogResponse =
158
+ await AIService.executeWithLogging(aiLogRequest);
159
+
160
+ return Response.sendJsonObjectResponse(req, res, {
161
+ note: response.content,
162
+ });
163
+ }
164
+ }
@@ -152,7 +152,6 @@ export class Service extends BaseService {
152
152
  const response: LLMCompletionResponse = await LLMService.getCompletion({
153
153
  llmProviderConfig: llmConfig,
154
154
  messages: request.messages,
155
- maxTokens: request.maxTokens || 4096,
156
155
  temperature: request.temperature ?? 0.7,
157
156
  });
158
157
 
@@ -2471,7 +2471,6 @@ ${incidentSeverity.name}
2471
2471
  const response: LLMCompletionResponse = await LLMService.getCompletion({
2472
2472
  llmProviderConfig: llmConfig,
2473
2473
  messages: aiContext.messages,
2474
- maxTokens: 8192,
2475
2474
  temperature: 0.7,
2476
2475
  });
2477
2476