@oneuptime/common 9.2.16 → 9.2.17

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 (115) hide show
  1. package/Models/DatabaseModels/CodeRepository.ts +664 -0
  2. package/Models/DatabaseModels/Index.ts +8 -0
  3. package/Models/DatabaseModels/LlmLog.ts +818 -0
  4. package/Models/DatabaseModels/LlmProvider.ts +21 -0
  5. package/Models/DatabaseModels/Project.ts +206 -0
  6. package/Models/DatabaseModels/ServiceCatalogCodeRepository.ts +549 -0
  7. package/Server/API/AIBillingAPI.ts +126 -0
  8. package/Server/API/GitHubAPI.ts +360 -0
  9. package/Server/API/IncidentAPI.ts +126 -0
  10. package/Server/EnvironmentConfig.ts +44 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1765580181582-MigrationName.ts +79 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1765633554715-MigrationName.ts +75 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1765801357168-MigrationName.ts +32 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1765810218488-MigrationName.ts +69 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1765830758857-MigrationName.ts +111 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1765834537501-MigrationName.ts +39 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
  18. package/Server/Services/AIBillingService.ts +247 -0
  19. package/Server/Services/AIService.ts +239 -0
  20. package/Server/Services/CodeRepositoryService.ts +10 -0
  21. package/Server/Services/IncidentService.ts +89 -0
  22. package/Server/Services/Index.ts +2 -0
  23. package/Server/Services/LlmLogService.ts +14 -0
  24. package/Server/Services/LlmProviderService.ts +58 -0
  25. package/Server/Services/ServiceCatalogCodeRepositoryService.ts +55 -0
  26. package/Server/Utils/AI/IncidentAIContextBuilder.ts +498 -0
  27. package/Server/Utils/CodeRepository/GitHub/GitHub.ts +226 -0
  28. package/Server/Utils/LLM/LLMService.ts +276 -0
  29. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +166 -0
  30. package/Server/Utils/Workspace/Slack/Slack.ts +134 -0
  31. package/Server/Utils/Workspace/Workspace.ts +126 -0
  32. package/Types/CodeRepository/CodeRepositoryType.ts +1 -1
  33. package/Types/LlmLogStatus.ts +7 -0
  34. package/Types/Permission.ts +87 -0
  35. package/Types/ServiceCatalog/CodeRepositoryImprovementAction.ts +9 -0
  36. package/UI/Components/AI/AILoader.tsx +95 -0
  37. package/UI/Components/AI/GenerateFromAIModal.tsx +295 -0
  38. package/UI/Components/Modal/Modal.tsx +6 -1
  39. package/build/dist/Models/DatabaseModels/CodeRepository.js +689 -0
  40. package/build/dist/Models/DatabaseModels/CodeRepository.js.map +1 -0
  41. package/build/dist/Models/DatabaseModels/Index.js +7 -0
  42. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  43. package/build/dist/Models/DatabaseModels/LlmLog.js +856 -0
  44. package/build/dist/Models/DatabaseModels/LlmLog.js.map +1 -0
  45. package/build/dist/Models/DatabaseModels/LlmProvider.js +22 -0
  46. package/build/dist/Models/DatabaseModels/LlmProvider.js.map +1 -1
  47. package/build/dist/Models/DatabaseModels/Project.js +220 -0
  48. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  49. package/build/dist/Models/DatabaseModels/ServiceCatalogCodeRepository.js +565 -0
  50. package/build/dist/Models/DatabaseModels/ServiceCatalogCodeRepository.js.map +1 -0
  51. package/build/dist/Server/API/AIBillingAPI.js +58 -0
  52. package/build/dist/Server/API/AIBillingAPI.js.map +1 -0
  53. package/build/dist/Server/API/GitHubAPI.js +207 -0
  54. package/build/dist/Server/API/GitHubAPI.js.map +1 -0
  55. package/build/dist/Server/API/IncidentAPI.js +84 -1
  56. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  57. package/build/dist/Server/EnvironmentConfig.js +31 -0
  58. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  59. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765580181582-MigrationName.js +34 -0
  60. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765580181582-MigrationName.js.map +1 -0
  61. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765633554715-MigrationName.js +32 -0
  62. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765633554715-MigrationName.js.map +1 -0
  63. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765801357168-MigrationName.js +38 -0
  64. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765801357168-MigrationName.js.map +1 -0
  65. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765810218488-MigrationName.js +30 -0
  66. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765810218488-MigrationName.js.map +1 -0
  67. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765830758857-MigrationName.js +44 -0
  68. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765830758857-MigrationName.js.map +1 -0
  69. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765834537501-MigrationName.js +22 -0
  70. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765834537501-MigrationName.js.map +1 -0
  71. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
  72. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  73. package/build/dist/Server/Services/AIBillingService.js +187 -0
  74. package/build/dist/Server/Services/AIBillingService.js.map +1 -0
  75. package/build/dist/Server/Services/AIService.js +185 -0
  76. package/build/dist/Server/Services/AIService.js.map +1 -0
  77. package/build/dist/Server/Services/CodeRepositoryService.js +9 -0
  78. package/build/dist/Server/Services/CodeRepositoryService.js.map +1 -0
  79. package/build/dist/Server/Services/IncidentService.js +61 -0
  80. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  81. package/build/dist/Server/Services/Index.js +2 -0
  82. package/build/dist/Server/Services/Index.js.map +1 -1
  83. package/build/dist/Server/Services/LlmLogService.js +13 -0
  84. package/build/dist/Server/Services/LlmLogService.js.map +1 -0
  85. package/build/dist/Server/Services/LlmProviderService.js +65 -0
  86. package/build/dist/Server/Services/LlmProviderService.js.map +1 -1
  87. package/build/dist/Server/Services/ServiceCatalogCodeRepositoryService.js +54 -0
  88. package/build/dist/Server/Services/ServiceCatalogCodeRepositoryService.js.map +1 -0
  89. package/build/dist/Server/Utils/AI/IncidentAIContextBuilder.js +408 -0
  90. package/build/dist/Server/Utils/AI/IncidentAIContextBuilder.js.map +1 -0
  91. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +163 -0
  92. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
  93. package/build/dist/Server/Utils/LLM/LLMService.js +225 -0
  94. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -0
  95. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +110 -0
  96. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  97. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +89 -0
  98. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  99. package/build/dist/Server/Utils/Workspace/Workspace.js +80 -0
  100. package/build/dist/Server/Utils/Workspace/Workspace.js.map +1 -1
  101. package/build/dist/Types/CodeRepository/CodeRepositoryType.js +1 -1
  102. package/build/dist/Types/CodeRepository/CodeRepositoryType.js.map +1 -1
  103. package/build/dist/Types/LlmLogStatus.js +8 -0
  104. package/build/dist/Types/LlmLogStatus.js.map +1 -0
  105. package/build/dist/Types/Permission.js +74 -0
  106. package/build/dist/Types/Permission.js.map +1 -1
  107. package/build/dist/Types/ServiceCatalog/CodeRepositoryImprovementAction.js +10 -0
  108. package/build/dist/Types/ServiceCatalog/CodeRepositoryImprovementAction.js.map +1 -0
  109. package/build/dist/UI/Components/AI/AILoader.js +64 -0
  110. package/build/dist/UI/Components/AI/AILoader.js.map +1 -0
  111. package/build/dist/UI/Components/AI/GenerateFromAIModal.js +207 -0
  112. package/build/dist/UI/Components/AI/GenerateFromAIModal.js.map +1 -0
  113. package/build/dist/UI/Components/Modal/Modal.js +6 -1
  114. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  115. package/package.json +1 -1
@@ -0,0 +1,498 @@
1
+ import ObjectID from "../../../Types/ObjectID";
2
+ import Incident from "../../../Models/DatabaseModels/Incident";
3
+ import IncidentStateTimeline from "../../../Models/DatabaseModels/IncidentStateTimeline";
4
+ import IncidentInternalNote from "../../../Models/DatabaseModels/IncidentInternalNote";
5
+ import IncidentPublicNote from "../../../Models/DatabaseModels/IncidentPublicNote";
6
+ import IncidentService from "../../Services/IncidentService";
7
+ import IncidentStateTimelineService from "../../Services/IncidentStateTimelineService";
8
+ import IncidentInternalNoteService from "../../Services/IncidentInternalNoteService";
9
+ import IncidentPublicNoteService from "../../Services/IncidentPublicNoteService";
10
+ import WorkspaceUtil, { WorkspaceChannelMessage } from "../Workspace/Workspace";
11
+ import WorkspaceProjectAuthTokenService from "../../Services/WorkspaceProjectAuthTokenService";
12
+ import WorkspaceProjectAuthToken from "../../../Models/DatabaseModels/WorkspaceProjectAuthToken";
13
+ import logger from "../Logger";
14
+ import CaptureSpan from "../Telemetry/CaptureSpan";
15
+ import OneUptimeDate from "../../../Types/Date";
16
+ import SortOrder from "../../../Types/BaseDatabase/SortOrder";
17
+ import { LLMMessage } from "../LLM/LLMService";
18
+ import NotificationRuleWorkspaceChannel from "../../../Types/Workspace/NotificationRules/NotificationRuleWorkspaceChannel";
19
+ import WorkspaceType from "../../../Types/Workspace/WorkspaceType";
20
+
21
+ export interface IncidentContextData {
22
+ incident: Incident;
23
+ stateTimeline: Array<IncidentStateTimeline>;
24
+ internalNotes: Array<IncidentInternalNote>;
25
+ publicNotes: Array<IncidentPublicNote>;
26
+ workspaceMessages: Array<WorkspaceChannelMessage>;
27
+ }
28
+
29
+ export interface AIGenerationContext {
30
+ contextText: string;
31
+ systemPrompt: string;
32
+ messages: Array<LLMMessage>;
33
+ }
34
+
35
+ export default class IncidentAIContextBuilder {
36
+ @CaptureSpan()
37
+ public static async buildIncidentContext(data: {
38
+ incidentId: ObjectID;
39
+ includeWorkspaceMessages?: boolean;
40
+ workspaceMessageLimit?: number;
41
+ }): Promise<IncidentContextData> {
42
+ const incident: Incident | null = await IncidentService.findOneById({
43
+ id: data.incidentId,
44
+ select: {
45
+ _id: true,
46
+ title: true,
47
+ description: true,
48
+ createdAt: true,
49
+ postmortemNote: true,
50
+ remediationNotes: true,
51
+ rootCause: true,
52
+ customFields: true,
53
+ projectId: true,
54
+ incidentSeverity: {
55
+ name: true,
56
+ color: true,
57
+ },
58
+ currentIncidentState: {
59
+ name: true,
60
+ color: true,
61
+ },
62
+ monitors: {
63
+ name: true,
64
+ },
65
+ labels: {
66
+ name: true,
67
+ color: true,
68
+ },
69
+ postUpdatesToWorkspaceChannels: true,
70
+ },
71
+ props: {
72
+ isRoot: true,
73
+ },
74
+ });
75
+
76
+ if (!incident) {
77
+ throw new Error("Incident not found");
78
+ }
79
+
80
+ // Fetch state timeline
81
+ const stateTimeline: Array<IncidentStateTimeline> =
82
+ await IncidentStateTimelineService.findBy({
83
+ query: {
84
+ incidentId: data.incidentId,
85
+ },
86
+ select: {
87
+ _id: true,
88
+ createdAt: true,
89
+ startsAt: true,
90
+ endsAt: true,
91
+ rootCause: true,
92
+ incidentState: {
93
+ name: true,
94
+ color: true,
95
+ },
96
+ createdByUser: {
97
+ name: true,
98
+ email: true,
99
+ },
100
+ },
101
+ sort: {
102
+ startsAt: SortOrder.Ascending,
103
+ },
104
+ limit: 100,
105
+ skip: 0,
106
+ props: {
107
+ isRoot: true,
108
+ },
109
+ });
110
+
111
+ // Fetch internal notes
112
+ const internalNotes: Array<IncidentInternalNote> =
113
+ await IncidentInternalNoteService.findBy({
114
+ query: {
115
+ incidentId: data.incidentId,
116
+ },
117
+ select: {
118
+ _id: true,
119
+ note: true,
120
+ createdAt: true,
121
+ createdByUser: {
122
+ name: true,
123
+ email: true,
124
+ },
125
+ },
126
+ sort: {
127
+ createdAt: SortOrder.Ascending,
128
+ },
129
+ limit: 100,
130
+ skip: 0,
131
+ props: {
132
+ isRoot: true,
133
+ },
134
+ });
135
+
136
+ // Fetch public notes
137
+ const publicNotes: Array<IncidentPublicNote> =
138
+ await IncidentPublicNoteService.findBy({
139
+ query: {
140
+ incidentId: data.incidentId,
141
+ },
142
+ select: {
143
+ _id: true,
144
+ note: true,
145
+ createdAt: true,
146
+ postedAt: true,
147
+ createdByUser: {
148
+ name: true,
149
+ email: true,
150
+ },
151
+ },
152
+ sort: {
153
+ createdAt: SortOrder.Ascending,
154
+ },
155
+ limit: 100,
156
+ skip: 0,
157
+ props: {
158
+ isRoot: true,
159
+ },
160
+ });
161
+
162
+ // Fetch workspace messages if requested and channels exist
163
+ let workspaceMessages: Array<WorkspaceChannelMessage> = [];
164
+
165
+ const workspaceChannels:
166
+ | Array<NotificationRuleWorkspaceChannel>
167
+ | undefined = incident.postUpdatesToWorkspaceChannels as
168
+ | Array<NotificationRuleWorkspaceChannel>
169
+ | undefined;
170
+
171
+ if (
172
+ data.includeWorkspaceMessages &&
173
+ workspaceChannels &&
174
+ workspaceChannels.length > 0 &&
175
+ incident.projectId
176
+ ) {
177
+ try {
178
+ const fetchParams: {
179
+ projectId: ObjectID;
180
+ workspaceChannels: Array<NotificationRuleWorkspaceChannel>;
181
+ limit?: number;
182
+ oldestTimestamp?: Date;
183
+ } = {
184
+ projectId: incident.projectId,
185
+ workspaceChannels: workspaceChannels,
186
+ limit: data.workspaceMessageLimit || 500,
187
+ };
188
+
189
+ if (incident.createdAt) {
190
+ fetchParams.oldestTimestamp = incident.createdAt;
191
+ }
192
+
193
+ workspaceMessages =
194
+ await this.getWorkspaceMessagesForIncident(fetchParams);
195
+ } catch (error) {
196
+ logger.error(`Error fetching workspace messages: ${error}`);
197
+ // Continue without workspace messages
198
+ }
199
+ }
200
+
201
+ return {
202
+ incident,
203
+ stateTimeline,
204
+ internalNotes,
205
+ publicNotes,
206
+ workspaceMessages,
207
+ };
208
+ }
209
+
210
+ @CaptureSpan()
211
+ public static formatIncidentContextForPostmortem(
212
+ contextData: IncidentContextData,
213
+ template?: string,
214
+ ): AIGenerationContext {
215
+ const {
216
+ incident,
217
+ stateTimeline,
218
+ internalNotes,
219
+ publicNotes,
220
+ workspaceMessages,
221
+ } = contextData;
222
+
223
+ let contextText: string = "";
224
+
225
+ // Basic incident information
226
+ contextText += "# Incident Information\n\n";
227
+ contextText += `**Title:** ${incident.title || "N/A"}\n\n`;
228
+ contextText += `**Description:** ${incident.description || "N/A"}\n\n`;
229
+ contextText += `**Severity:** ${incident.incidentSeverity?.name || "N/A"}\n\n`;
230
+ contextText += `**Current State:** ${incident.currentIncidentState?.name || "N/A"}\n\n`;
231
+ contextText += `**Created At:** ${incident.createdAt ? OneUptimeDate.getDateAsFormattedString(incident.createdAt) : "N/A"}\n\n`;
232
+
233
+ // Affected monitors
234
+ if (incident.monitors && incident.monitors.length > 0) {
235
+ contextText += "**Affected Monitors:** ";
236
+ contextText += incident.monitors
237
+ .map((m: { name?: string }) => {
238
+ return m.name;
239
+ })
240
+ .join(", ");
241
+ contextText += "\n\n";
242
+ }
243
+
244
+ // Labels
245
+ if (incident.labels && incident.labels.length > 0) {
246
+ contextText += "**Labels:** ";
247
+ contextText += incident.labels
248
+ .map((l: { name?: string }) => {
249
+ return l.name;
250
+ })
251
+ .join(", ");
252
+ contextText += "\n\n";
253
+ }
254
+
255
+ // Root cause if available
256
+ if (incident.rootCause) {
257
+ contextText += `**Root Cause:** ${incident.rootCause}\n\n`;
258
+ }
259
+
260
+ // Remediation notes if available
261
+ if (incident.remediationNotes) {
262
+ contextText += `**Remediation Notes:** ${incident.remediationNotes}\n\n`;
263
+ }
264
+
265
+ // State timeline
266
+ if (stateTimeline.length > 0) {
267
+ contextText += "# State Timeline\n\n";
268
+ for (const timeline of stateTimeline) {
269
+ const startTime: string = timeline.startsAt
270
+ ? OneUptimeDate.getDateAsFormattedString(timeline.startsAt)
271
+ : "N/A";
272
+ const stateName: string =
273
+ timeline.incidentState?.name?.toString() || "Unknown";
274
+ const createdBy: string =
275
+ timeline.createdByUser?.name?.toString() ||
276
+ timeline.createdByUser?.email?.toString() ||
277
+ "System";
278
+
279
+ contextText += `- **${startTime}**: State changed to **${stateName}** by ${createdBy}\n`;
280
+ if (timeline.rootCause) {
281
+ contextText += ` - Root cause noted: ${timeline.rootCause}\n`;
282
+ }
283
+ }
284
+ contextText += "\n";
285
+ }
286
+
287
+ // Internal notes
288
+ if (internalNotes.length > 0) {
289
+ contextText += "# Internal Notes (Private)\n\n";
290
+ for (const note of internalNotes) {
291
+ const noteTime: string = note.createdAt
292
+ ? OneUptimeDate.getDateAsFormattedString(note.createdAt)
293
+ : "N/A";
294
+ const createdBy: string =
295
+ note.createdByUser?.name?.toString() ||
296
+ note.createdByUser?.email?.toString() ||
297
+ "Unknown";
298
+
299
+ contextText += `**[${noteTime}] ${createdBy}:**\n`;
300
+ contextText += `${note.note || "N/A"}\n\n`;
301
+ }
302
+ }
303
+
304
+ // Public notes
305
+ if (publicNotes.length > 0) {
306
+ contextText += "# Public Notes\n\n";
307
+ for (const note of publicNotes) {
308
+ const noteTime: string = note.postedAt
309
+ ? OneUptimeDate.getDateAsFormattedString(note.postedAt)
310
+ : note.createdAt
311
+ ? OneUptimeDate.getDateAsFormattedString(note.createdAt)
312
+ : "N/A";
313
+ const createdBy: string =
314
+ note.createdByUser?.name?.toString() ||
315
+ note.createdByUser?.email?.toString() ||
316
+ "Unknown";
317
+
318
+ contextText += `**[${noteTime}] ${createdBy}:**\n`;
319
+ contextText += `${note.note || "N/A"}\n\n`;
320
+ }
321
+ }
322
+
323
+ // Workspace messages (Slack/Teams)
324
+ if (workspaceMessages.length > 0) {
325
+ contextText += "# Discussion from Incident Channel\n\n";
326
+ contextText += WorkspaceUtil.formatMessagesAsContext(workspaceMessages, {
327
+ includeTimestamp: true,
328
+ includeUsername: true,
329
+ maxLength: 30000,
330
+ });
331
+ contextText += "\n\n";
332
+ }
333
+
334
+ // System prompt for postmortem generation
335
+ let systemPrompt: string;
336
+
337
+ if (template) {
338
+ // When a template is provided, strictly fill only the template
339
+ systemPrompt = `You are an expert Site Reliability Engineer (SRE) and incident response specialist. Your task is to fill in an incident postmortem template based on the provided incident data.
340
+
341
+ CRITICAL INSTRUCTIONS:
342
+ - You MUST use ONLY the exact template structure provided below
343
+ - Fill in each section of the template with relevant information from the incident data
344
+ - Do NOT add any new sections, headers, or content that is not part of the template
345
+ - Do NOT add introductions, conclusions, or any text outside the template structure
346
+ - If a section in the template has no relevant data, write "No data available" or leave the placeholder text
347
+ - Be blameless - focus on systemic improvements rather than individual blame
348
+ - Write in a professional, clear, and concise manner
349
+
350
+ TEMPLATE TO FILL (use this exact structure):
351
+
352
+ ${template}`;
353
+ } else {
354
+ // When no template is provided, use standard format
355
+ systemPrompt = `You are an expert Site Reliability Engineer (SRE) and incident response specialist. Your task is to generate a comprehensive, well-structured incident postmortem based on the provided incident data.
356
+
357
+ The postmortem should:
358
+ 1. Be written in a blameless manner, focusing on systemic improvements rather than individual blame
359
+ 2. Include a clear executive summary
360
+ 3. Provide a detailed timeline of events
361
+ 4. Identify the root cause(s) and contributing factors
362
+ 5. Outline the impact on users and systems
363
+ 6. List actionable items to prevent recurrence
364
+ 7. Include lessons learned
365
+
366
+ Use a standard incident postmortem format with sections for: Executive Summary, Timeline, Root Cause Analysis, Impact, Action Items, and Lessons Learned.
367
+
368
+ Write in a professional, clear, and concise manner. Use markdown formatting for better readability.`;
369
+ }
370
+
371
+ // Build user message based on whether template is provided
372
+ const userMessage: string = template
373
+ ? `Fill in the template above using ONLY the following incident data. Output only the filled template, nothing else:\n\n${contextText}`
374
+ : `Based on the following incident data, please generate a comprehensive incident postmortem:\n\n${contextText}`;
375
+
376
+ // Build messages array
377
+ const messages: Array<LLMMessage> = [
378
+ {
379
+ role: "system",
380
+ content: systemPrompt,
381
+ },
382
+ {
383
+ role: "user",
384
+ content: userMessage,
385
+ },
386
+ ];
387
+
388
+ return {
389
+ contextText,
390
+ systemPrompt,
391
+ messages,
392
+ };
393
+ }
394
+
395
+ @CaptureSpan()
396
+ public static buildGenericAIContext(data: {
397
+ systemPrompt: string;
398
+ userPrompt: string;
399
+ context?: string;
400
+ }): AIGenerationContext {
401
+ let userContent: string = data.userPrompt;
402
+
403
+ if (data.context) {
404
+ userContent = `${data.userPrompt}\n\nContext:\n${data.context}`;
405
+ }
406
+
407
+ const messages: Array<LLMMessage> = [
408
+ {
409
+ role: "system",
410
+ content: data.systemPrompt,
411
+ },
412
+ {
413
+ role: "user",
414
+ content: userContent,
415
+ },
416
+ ];
417
+
418
+ return {
419
+ contextText: data.context || "",
420
+ systemPrompt: data.systemPrompt,
421
+ messages,
422
+ };
423
+ }
424
+
425
+ @CaptureSpan()
426
+ private static async getWorkspaceMessagesForIncident(data: {
427
+ projectId: ObjectID;
428
+ workspaceChannels: Array<NotificationRuleWorkspaceChannel>;
429
+ limit?: number;
430
+ oldestTimestamp?: Date;
431
+ }): Promise<Array<WorkspaceChannelMessage>> {
432
+ const allMessages: Array<WorkspaceChannelMessage> = [];
433
+
434
+ for (const channel of data.workspaceChannels) {
435
+ try {
436
+ // Get auth token for this workspace type
437
+ const projectAuth: WorkspaceProjectAuthToken | null =
438
+ await WorkspaceProjectAuthTokenService.getProjectAuth({
439
+ projectId: data.projectId,
440
+ workspaceType: channel.workspaceType,
441
+ });
442
+
443
+ if (!projectAuth || !projectAuth.authToken) {
444
+ logger.debug(
445
+ `No auth token found for workspace type: ${channel.workspaceType}`,
446
+ );
447
+ continue;
448
+ }
449
+
450
+ const messagesParams: {
451
+ channelId: string;
452
+ authToken: string;
453
+ projectId: ObjectID;
454
+ workspaceType: WorkspaceType;
455
+ teamId?: string;
456
+ limit?: number;
457
+ oldestTimestamp?: Date;
458
+ } = {
459
+ channelId: channel.id,
460
+ authToken: projectAuth.authToken,
461
+ projectId: data.projectId,
462
+ workspaceType: channel.workspaceType,
463
+ };
464
+
465
+ if (channel.teamId) {
466
+ messagesParams.teamId = channel.teamId;
467
+ }
468
+
469
+ if (data.limit !== undefined) {
470
+ messagesParams.limit = data.limit;
471
+ }
472
+
473
+ if (data.oldestTimestamp) {
474
+ messagesParams.oldestTimestamp = data.oldestTimestamp;
475
+ }
476
+
477
+ const messages: Array<WorkspaceChannelMessage> =
478
+ await WorkspaceUtil.getChannelMessages(messagesParams);
479
+
480
+ allMessages.push(...messages);
481
+ } catch (error) {
482
+ logger.error(
483
+ `Error fetching messages from channel ${channel.id}: ${error}`,
484
+ );
485
+ // Continue with other channels even if one fails
486
+ }
487
+ }
488
+
489
+ // Sort all messages by timestamp
490
+ allMessages.sort(
491
+ (a: WorkspaceChannelMessage, b: WorkspaceChannelMessage) => {
492
+ return a.timestamp.getTime() - b.timestamp.getTime();
493
+ },
494
+ );
495
+
496
+ return allMessages;
497
+ }
498
+ }