@oneuptime/common 11.3.24 → 11.3.26
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.
- package/Models/DatabaseModels/AIConversation.ts +322 -0
- package/Models/DatabaseModels/AIConversationMessage.ts +495 -0
- package/Models/DatabaseModels/AIRun.ts +584 -0
- package/Models/DatabaseModels/AIRunEvent.ts +443 -0
- package/Models/DatabaseModels/Index.ts +8 -0
- package/Models/DatabaseModels/LlmLog.ts +26 -0
- package/Models/DatabaseModels/LlmProvider.ts +2 -2
- package/Server/API/AIChatAPI.ts +693 -0
- package/Server/API/LlmProviderAPI.ts +169 -0
- package/Server/API/MicrosoftTeamsAPI.ts +5 -0
- package/Server/API/SlackAPI.ts +663 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783363279075-AddAIChatModels.ts +245 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783443471795-AddLlmProviderToAIConversation.ts +36 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783453297388-AddAIChatWriteActionsAndWidgets.ts +46 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783461767405-MigrationName.ts +119 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Services/AIConversationMessageService.ts +39 -0
- package/Server/Services/AIConversationService.ts +63 -0
- package/Server/Services/AIRunEventService.ts +39 -0
- package/Server/Services/AIRunService.ts +39 -0
- package/Server/Services/AIService.ts +53 -31
- package/Server/Services/DatabaseService.ts +19 -0
- package/Server/Services/LlmProviderService.ts +110 -0
- package/Server/Services/ProjectService.ts +24 -0
- package/Server/Types/AnalyticsDatabase/ModelPermission.ts +74 -9
- package/Server/Utils/AI/AIChatPrivacyFilter.ts +28 -0
- package/Server/Utils/AI/Chat/ChatAgentRunner.ts +1054 -0
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +239 -0
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +51 -0
- package/Server/Utils/AI/Toolbox/AlertTools.ts +201 -0
- package/Server/Utils/AI/Toolbox/AlertWriteTools.ts +174 -0
- package/Server/Utils/AI/Toolbox/ContextTools.ts +189 -0
- package/Server/Utils/AI/Toolbox/ExceptionTools.ts +149 -0
- package/Server/Utils/AI/Toolbox/IncidentTools.ts +204 -0
- package/Server/Utils/AI/Toolbox/IncidentWriteTools.ts +350 -0
- package/Server/Utils/AI/Toolbox/Index.ts +228 -0
- package/Server/Utils/AI/Toolbox/LogTools.ts +339 -0
- package/Server/Utils/AI/Toolbox/MetricTools.ts +179 -0
- package/Server/Utils/AI/Toolbox/MonitorTools.ts +193 -0
- package/Server/Utils/AI/Toolbox/Serializer.ts +257 -0
- package/Server/Utils/AI/Toolbox/ToolTypes.ts +249 -0
- package/Server/Utils/AI/Toolbox/TraceTools.ts +402 -0
- package/Server/Utils/AI/Toolbox/WidgetBuilder.ts +189 -0
- package/Server/Utils/LLM/LLMService.ts +502 -92
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +512 -19
- package/Server/Utils/Workspace/Slack/Slack.ts +80 -0
- package/Server/Utils/Workspace/Slack/app-manifest.json +16 -1
- package/Tests/Server/Utils/AI/AIChatModelACL.test.ts +42 -0
- package/Tests/Server/Utils/AI/ChatAgentHelpers.test.ts +89 -0
- package/Tests/Server/Utils/AI/LLMServiceBaseUrl.test.ts +139 -0
- package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +308 -0
- package/Tests/Server/Utils/AI/ToolArgsGetTimeRange.test.ts +62 -0
- package/Tests/Server/Utils/AI/ToolArgsScopeServiceIds.test.ts +79 -0
- package/Tests/Server/Utils/AI/ToolResultSerializer.test.ts +155 -0
- package/Tests/Types/Billing/SubscriptionStatus.test.ts +127 -0
- package/Tests/Types/Metrics/RecordingRuleDefinition.test.ts +213 -0
- package/Types/AI/AIChatMessageRole.ts +6 -0
- package/Types/AI/AIChatMessageStatus.ts +32 -0
- package/Types/AI/AIChatPermissionMode.ts +69 -0
- package/Types/AI/AIChatTypes.ts +231 -0
- package/Types/AI/AIRunEventType.ts +17 -0
- package/Types/AI/AIRunStatus.ts +28 -0
- package/Types/AI/AIRunType.ts +6 -0
- package/Types/LLM/LlmType.ts +6 -0
- package/UI/Components/Header/HeaderIconDropdownButton.tsx +22 -12
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +35 -3
- package/UI/Components/Page/Page.tsx +14 -0
- package/UI/Utils/LlmTypeDropdownOptions.ts +40 -0
- package/UI/Utils/TestLLMProvider.ts +59 -0
- package/build/dist/Models/DatabaseModels/AIConversation.js +345 -0
- package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -0
- package/build/dist/Models/DatabaseModels/AIConversationMessage.js +521 -0
- package/build/dist/Models/DatabaseModels/AIConversationMessage.js.map +1 -0
- package/build/dist/Models/DatabaseModels/AIRun.js +619 -0
- package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -0
- package/build/dist/Models/DatabaseModels/AIRunEvent.js +469 -0
- package/build/dist/Models/DatabaseModels/AIRunEvent.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Index.js +8 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LlmLog.js +28 -0
- package/build/dist/Models/DatabaseModels/LlmLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LlmProvider.js +2 -2
- package/build/dist/Models/DatabaseModels/LlmProvider.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +498 -0
- package/build/dist/Server/API/AIChatAPI.js.map +1 -0
- package/build/dist/Server/API/LlmProviderAPI.js +107 -1
- package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +4 -0
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +442 -0
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783363279075-AddAIChatModels.js +96 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783363279075-AddAIChatModels.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783443471795-AddLlmProviderToAIConversation.js +25 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783443471795-AddLlmProviderToAIConversation.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783453297388-AddAIChatWriteActionsAndWidgets.js +31 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783453297388-AddAIChatWriteActionsAndWidgets.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783461767405-MigrationName.js +46 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783461767405-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AIConversationMessageService.js +34 -0
- package/build/dist/Server/Services/AIConversationMessageService.js.map +1 -0
- package/build/dist/Server/Services/AIConversationService.js +42 -0
- package/build/dist/Server/Services/AIConversationService.js.map +1 -0
- package/build/dist/Server/Services/AIRunEventService.js +34 -0
- package/build/dist/Server/Services/AIRunEventService.js.map +1 -0
- package/build/dist/Server/Services/AIRunService.js +34 -0
- package/build/dist/Server/Services/AIRunService.js.map +1 -0
- package/build/dist/Server/Services/AIService.js +31 -23
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +10 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/LlmProviderService.js +108 -0
- package/build/dist/Server/Services/LlmProviderService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +25 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +50 -7
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
- package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js +18 -0
- package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js.map +1 -0
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +745 -0
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +165 -0
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -0
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +44 -0
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +167 -0
- package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/AlertWriteTools.js +136 -0
- package/build/dist/Server/Utils/AI/Toolbox/AlertWriteTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/ContextTools.js +141 -0
- package/build/dist/Server/Utils/AI/Toolbox/ContextTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/ExceptionTools.js +117 -0
- package/build/dist/Server/Utils/AI/Toolbox/ExceptionTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js +167 -0
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/IncidentWriteTools.js +280 -0
- package/build/dist/Server/Utils/AI/Toolbox/IncidentWriteTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js +150 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/LogTools.js +246 -0
- package/build/dist/Server/Utils/AI/Toolbox/LogTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js +120 -0
- package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js +158 -0
- package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/Serializer.js +188 -0
- package/build/dist/Server/Utils/AI/Toolbox/Serializer.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/ToolTypes.js +142 -0
- package/build/dist/Server/Utils/AI/Toolbox/ToolTypes.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/TraceTools.js +309 -0
- package/build/dist/Server/Utils/AI/Toolbox/TraceTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/WidgetBuilder.js +120 -0
- package/build/dist/Server/Utils/AI/Toolbox/WidgetBuilder.js.map +1 -0
- package/build/dist/Server/Utils/LLM/LLMService.js +372 -88
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +378 -15
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +59 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/app-manifest.json +16 -1
- package/build/dist/Types/AI/AIChatMessageRole.js +7 -0
- package/build/dist/Types/AI/AIChatMessageRole.js.map +1 -0
- package/build/dist/Types/AI/AIChatMessageStatus.js +27 -0
- package/build/dist/Types/AI/AIChatMessageStatus.js.map +1 -0
- package/build/dist/Types/AI/AIChatPermissionMode.js +53 -0
- package/build/dist/Types/AI/AIChatPermissionMode.js.map +1 -0
- package/build/dist/Types/AI/AIChatTypes.js +74 -0
- package/build/dist/Types/AI/AIChatTypes.js.map +1 -0
- package/build/dist/Types/AI/AIRunEventType.js +18 -0
- package/build/dist/Types/AI/AIRunEventType.js.map +1 -0
- package/build/dist/Types/AI/AIRunStatus.js +26 -0
- package/build/dist/Types/AI/AIRunStatus.js.map +1 -0
- package/build/dist/Types/AI/AIRunType.js +7 -0
- package/build/dist/Types/AI/AIRunType.js.map +1 -0
- package/build/dist/Types/LLM/LlmType.js +6 -0
- package/build/dist/Types/LLM/LlmType.js.map +1 -1
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +10 -5
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +18 -4
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Page/Page.js +3 -1
- package/build/dist/UI/Components/Page/Page.js.map +1 -1
- package/build/dist/UI/Utils/LlmTypeDropdownOptions.js +38 -0
- package/build/dist/UI/Utils/LlmTypeDropdownOptions.js.map +1 -0
- package/build/dist/UI/Utils/TestLLMProvider.js +37 -0
- package/build/dist/UI/Utils/TestLLMProvider.js.map +1 -0
- package/package.json +1 -1
|
@@ -39,6 +39,7 @@ import MicrosoftTeamsIncidentEpisodeActions from "./Actions/IncidentEpisode";
|
|
|
39
39
|
import MicrosoftTeamsMonitorActions from "./Actions/Monitor";
|
|
40
40
|
import MicrosoftTeamsScheduledMaintenanceActions from "./Actions/ScheduledMaintenance";
|
|
41
41
|
import MicrosoftTeamsOnCallDutyActions from "./Actions/OnCallDutyPolicy";
|
|
42
|
+
import AccessTokenService from "../../../Services/AccessTokenService";
|
|
42
43
|
// Microsoft Teams apps should always be single-tenant
|
|
43
44
|
const MICROSOFT_TEAMS_APP_TYPE = "SingleTenant";
|
|
44
45
|
// Maximum number of pages to fetch when paginating teams
|
|
@@ -1215,7 +1216,7 @@ class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
1215
1216
|
}
|
|
1216
1217
|
// Bot Framework specific methods
|
|
1217
1218
|
static async handleBotMessageActivity(data) {
|
|
1218
|
-
var _a, _b, _c;
|
|
1219
|
+
var _a, _b, _c, _d;
|
|
1219
1220
|
// Handle direct messages to bot or @mentions via Bot Framework
|
|
1220
1221
|
const messageText = data.activity["text"] || "";
|
|
1221
1222
|
const possibleActionValue = data.activity["value"] || {};
|
|
@@ -1228,9 +1229,23 @@ class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
1228
1229
|
logger.debug(`Conversation: ${JSON.stringify(conversation)}`);
|
|
1229
1230
|
logger.debug(`Channel data: ${JSON.stringify(channelData)}`);
|
|
1230
1231
|
logger.debug(`Entities: ${JSON.stringify(entities)}`);
|
|
1232
|
+
/*
|
|
1233
|
+
* Loop-guard: never process the bot's own messages. Teams generally does not
|
|
1234
|
+
* echo the bot to itself, but if the sender is the bot recipient (same id) or
|
|
1235
|
+
* is flagged with the "bot" role, ignore the activity to avoid a self-reply
|
|
1236
|
+
* loop when routing free-form text to the AI assistant.
|
|
1237
|
+
*/
|
|
1238
|
+
const senderId = from["id"] || "";
|
|
1239
|
+
const botRecipientId = (_a = data.activity["recipient"]) === null || _a === void 0 ? void 0 : _a["id"];
|
|
1240
|
+
const senderRole = from["role"] || "";
|
|
1241
|
+
if (senderRole === "bot" ||
|
|
1242
|
+
(senderId && botRecipientId && senderId === botRecipientId)) {
|
|
1243
|
+
logger.debug("Message activity originates from the bot itself; ignoring to prevent a loop");
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1231
1246
|
// If this is actually an Adaptive Card submit wrapped as a message, route to invoke handler
|
|
1232
1247
|
if (possibleActionValue["action"] ||
|
|
1233
|
-
((
|
|
1248
|
+
((_b = possibleActionValue["data"]) === null || _b === void 0 ? void 0 : _b["action"])) {
|
|
1234
1249
|
logger.debug("Message activity contains action payload; routing to invoke handler");
|
|
1235
1250
|
await this.handleBotInvokeActivity({
|
|
1236
1251
|
activity: data.activity,
|
|
@@ -1239,7 +1254,7 @@ class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
1239
1254
|
return;
|
|
1240
1255
|
}
|
|
1241
1256
|
// Check if the bot was mentioned
|
|
1242
|
-
const recipientId = (
|
|
1257
|
+
const recipientId = (_c = data.activity["recipient"]) === null || _c === void 0 ? void 0 : _c["id"];
|
|
1243
1258
|
const conversationType = conversation["conversationType"] || "";
|
|
1244
1259
|
const isDirectMessage = conversationType === "personal";
|
|
1245
1260
|
const isMentioned = entities.some((entity) => {
|
|
@@ -1253,7 +1268,7 @@ class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
1253
1268
|
return;
|
|
1254
1269
|
}
|
|
1255
1270
|
// Extract tenant ID to get project ID
|
|
1256
|
-
const tenantId = (
|
|
1271
|
+
const tenantId = (_d = channelData["tenant"]) === null || _d === void 0 ? void 0 : _d["id"];
|
|
1257
1272
|
if (!tenantId) {
|
|
1258
1273
|
logger.error("Tenant ID not found in channelData");
|
|
1259
1274
|
await data.turnContext.sendActivity("Sorry, I couldn't identify your organization. Please try again later.");
|
|
@@ -1287,13 +1302,40 @@ class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
1287
1302
|
.replace(/<at[^>]*>.*?<\/at>/g, "")
|
|
1288
1303
|
.trim()
|
|
1289
1304
|
.toLowerCase();
|
|
1305
|
+
/*
|
|
1306
|
+
* Preserve the original casing of the user's question. The AI assistant
|
|
1307
|
+
* should receive the free-form text exactly as the user typed it (case,
|
|
1308
|
+
* punctuation and spacing all matter), with only the bot @mention stripped.
|
|
1309
|
+
*/
|
|
1310
|
+
const originalQuestionText = messageText
|
|
1311
|
+
.replace(/<at[^>]*>.*?<\/at>/g, "")
|
|
1312
|
+
.trim();
|
|
1290
1313
|
let responseText = "";
|
|
1291
1314
|
try {
|
|
1292
1315
|
const isCreateIncidentCommand = cleanText === "create incident" ||
|
|
1293
1316
|
cleanText.startsWith("create incident ");
|
|
1294
1317
|
const isCreateMaintenanceCommand = cleanText === "create maintenance" ||
|
|
1295
1318
|
cleanText.startsWith("create maintenance ");
|
|
1296
|
-
|
|
1319
|
+
/*
|
|
1320
|
+
* Explicit commands are matched precisely so that natural-language
|
|
1321
|
+
* questions (which may incidentally contain words like "help" or
|
|
1322
|
+
* "alerts") fall through to the AI assistant instead of a canned command.
|
|
1323
|
+
*/
|
|
1324
|
+
const isHelpCommand = cleanText === "help" || cleanText === "" || cleanText === "?";
|
|
1325
|
+
const isShowActiveIncidentsCommand = cleanText === "show active incidents" ||
|
|
1326
|
+
cleanText === "active incidents";
|
|
1327
|
+
const isShowScheduledMaintenanceCommand = cleanText === "show scheduled maintenance" ||
|
|
1328
|
+
cleanText === "scheduled maintenance";
|
|
1329
|
+
const isShowOngoingMaintenanceCommand = cleanText === "show ongoing maintenance" ||
|
|
1330
|
+
cleanText === "ongoing maintenance";
|
|
1331
|
+
const isShowActiveAlertsCommand = cleanText === "show active alerts" || cleanText === "active alerts";
|
|
1332
|
+
/*
|
|
1333
|
+
* "ask <question>" is an explicit prefix that always routes to the AI
|
|
1334
|
+
* assistant. When present, strip the prefix and use the remainder as the
|
|
1335
|
+
* question.
|
|
1336
|
+
*/
|
|
1337
|
+
const isAskCommand = cleanText === "ask" || cleanText.startsWith("ask ");
|
|
1338
|
+
if (isHelpCommand) {
|
|
1297
1339
|
responseText = this.getHelpMessage();
|
|
1298
1340
|
}
|
|
1299
1341
|
else if (isCreateIncidentCommand) {
|
|
@@ -1326,24 +1368,40 @@ class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
1326
1368
|
logger.debug("New scheduled maintenance card sent successfully");
|
|
1327
1369
|
return;
|
|
1328
1370
|
}
|
|
1329
|
-
else if (
|
|
1330
|
-
cleanText.includes("active incidents")) {
|
|
1371
|
+
else if (isShowActiveIncidentsCommand) {
|
|
1331
1372
|
responseText = await this.getActiveIncidentsMessage(projectId);
|
|
1332
1373
|
}
|
|
1333
|
-
else if (
|
|
1334
|
-
cleanText.includes("scheduled maintenance")) {
|
|
1374
|
+
else if (isShowScheduledMaintenanceCommand) {
|
|
1335
1375
|
responseText = await this.getScheduledMaintenanceMessage(projectId);
|
|
1336
1376
|
}
|
|
1337
|
-
else if (
|
|
1338
|
-
cleanText.includes("ongoing maintenance")) {
|
|
1377
|
+
else if (isShowOngoingMaintenanceCommand) {
|
|
1339
1378
|
responseText = await this.getOngoingMaintenanceMessage(projectId);
|
|
1340
1379
|
}
|
|
1341
|
-
else if (
|
|
1342
|
-
cleanText.includes("active alerts")) {
|
|
1380
|
+
else if (isShowActiveAlertsCommand) {
|
|
1343
1381
|
responseText = await this.getActiveAlertsMessage(projectId);
|
|
1344
1382
|
}
|
|
1345
1383
|
else {
|
|
1346
|
-
|
|
1384
|
+
/*
|
|
1385
|
+
* AI Ops: any message that is not one of the explicit commands above is
|
|
1386
|
+
* treated as a natural-language question for the observability
|
|
1387
|
+
* assistant. This also handles the explicit "ask <question>" prefix.
|
|
1388
|
+
*/
|
|
1389
|
+
const question = isAskCommand
|
|
1390
|
+
? originalQuestionText.replace(/^ask\s*/i, "").trim()
|
|
1391
|
+
: originalQuestionText;
|
|
1392
|
+
if (!question) {
|
|
1393
|
+
// Bare "ask" with no question - point the user at help.
|
|
1394
|
+
responseText = this.getHelpMessage();
|
|
1395
|
+
await data.turnContext.sendActivity(responseText);
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
await this.answerObservabilityQuestion({
|
|
1399
|
+
activity: data.activity,
|
|
1400
|
+
turnContext: data.turnContext,
|
|
1401
|
+
projectId: projectId,
|
|
1402
|
+
question: question,
|
|
1403
|
+
});
|
|
1404
|
+
return;
|
|
1347
1405
|
}
|
|
1348
1406
|
// Send response directly using TurnContext - this is the recommended Bot Framework pattern
|
|
1349
1407
|
await data.turnContext.sendActivity(responseText);
|
|
@@ -1359,12 +1417,290 @@ class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
1359
1417
|
throw error;
|
|
1360
1418
|
}
|
|
1361
1419
|
}
|
|
1420
|
+
/*
|
|
1421
|
+
* AI Ops: strip a Teams message body down to plain text - remove <at> bot
|
|
1422
|
+
* mentions and any remaining HTML tags, collapse whitespace, and trim. Teams
|
|
1423
|
+
* channel/chat message bodies are typically HTML (body.contentType "html").
|
|
1424
|
+
*/
|
|
1425
|
+
static toPlainTextFromTeamsMessageBody(rawContent) {
|
|
1426
|
+
return rawContent
|
|
1427
|
+
.replace(/<at[^>]*>.*?<\/at>/g, "")
|
|
1428
|
+
.replace(/<[^>]*>/g, "")
|
|
1429
|
+
.replace(/ /g, " ")
|
|
1430
|
+
.replace(/\s+/g, " ")
|
|
1431
|
+
.trim();
|
|
1432
|
+
}
|
|
1433
|
+
/*
|
|
1434
|
+
* AI Ops: gather the prior turns of the current Teams conversation/thread and
|
|
1435
|
+
* map them to the assistant's history shape ({ role, content }, oldest-first,
|
|
1436
|
+
* excluding the current triggering message).
|
|
1437
|
+
*
|
|
1438
|
+
* Teams limitation: in channels a bot only receives messages that @mention it
|
|
1439
|
+
* (unless RSC / ChannelMessage.Read.Group is granted at install time), so
|
|
1440
|
+
* channel follow-ups generally require re-@mentioning the bot. 1:1 (personal)
|
|
1441
|
+
* chat follow-ups do not require a mention. If ids can't be parsed or the
|
|
1442
|
+
* Graph call fails, we degrade gracefully to no history - the caller wraps
|
|
1443
|
+
* this in a try/catch and never lets a history failure break the reply.
|
|
1444
|
+
*/
|
|
1445
|
+
static async getConversationHistoryTurns(data) {
|
|
1446
|
+
var _a, _b, _c;
|
|
1447
|
+
const { activity, projectId } = data;
|
|
1448
|
+
const conversation = activity["conversation"] || {};
|
|
1449
|
+
const channelData = activity["channelData"] || {};
|
|
1450
|
+
const conversationType = conversation["conversationType"] || "";
|
|
1451
|
+
const conversationId = conversation["id"] || "";
|
|
1452
|
+
const currentMessageId = activity["id"] || "";
|
|
1453
|
+
/*
|
|
1454
|
+
* The bot's id - used to classify each message as "assistant" (from the
|
|
1455
|
+
* bot) vs "user". This is the same id the loop-guard compares against.
|
|
1456
|
+
*/
|
|
1457
|
+
const botId = ((_a = activity["recipient"]) === null || _a === void 0 ? void 0 : _a["id"]) ||
|
|
1458
|
+
MicrosoftTeamsAppClientId ||
|
|
1459
|
+
"";
|
|
1460
|
+
if (!conversationId) {
|
|
1461
|
+
return [];
|
|
1462
|
+
}
|
|
1463
|
+
// Acquire a Graph app token using the same mechanism the rest of this file uses.
|
|
1464
|
+
const accessToken = await this.getValidAccessToken({
|
|
1465
|
+
authToken: "",
|
|
1466
|
+
projectId: projectId,
|
|
1467
|
+
});
|
|
1468
|
+
// Collect raw Graph message objects (unordered; we sort at the end).
|
|
1469
|
+
let rawMessages = [];
|
|
1470
|
+
if (conversationType === "personal") {
|
|
1471
|
+
/*
|
|
1472
|
+
* 1:1 chat: conversation.id is the chat id. Fetch recent chat messages.
|
|
1473
|
+
*/
|
|
1474
|
+
const chatId = conversationId;
|
|
1475
|
+
const response = await API.get({
|
|
1476
|
+
url: URL.fromString(`https://graph.microsoft.com/v1.0/chats/${encodeURIComponent(chatId)}/messages?$top=20`),
|
|
1477
|
+
headers: {
|
|
1478
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1479
|
+
"Content-Type": "application/json",
|
|
1480
|
+
},
|
|
1481
|
+
});
|
|
1482
|
+
if (response instanceof HTTPErrorResponse) {
|
|
1483
|
+
logger.debug("Failed to fetch 1:1 chat history for AI Ops context");
|
|
1484
|
+
logger.debug(response);
|
|
1485
|
+
return [];
|
|
1486
|
+
}
|
|
1487
|
+
rawMessages = response.data["value"] || [];
|
|
1488
|
+
}
|
|
1489
|
+
else {
|
|
1490
|
+
/*
|
|
1491
|
+
* Channel thread: conversation.id encodes the root message id as
|
|
1492
|
+
* "<channelId>;messageid=<rootId>". Team & channel ids come from
|
|
1493
|
+
* channelData. Fetch the root message plus its replies.
|
|
1494
|
+
*/
|
|
1495
|
+
const teamId = (_b = channelData["team"]) === null || _b === void 0 ? void 0 : _b["id"];
|
|
1496
|
+
const channelId = (_c = channelData["channel"]) === null || _c === void 0 ? void 0 : _c["id"];
|
|
1497
|
+
const messageIdMatch = conversationId.match(/messageid=(\d+)/);
|
|
1498
|
+
const rootMessageId = messageIdMatch === null || messageIdMatch === void 0 ? void 0 : messageIdMatch[1];
|
|
1499
|
+
if (!teamId || !channelId || !rootMessageId) {
|
|
1500
|
+
logger.debug("Could not parse team/channel/root message ids for AI Ops channel history; proceeding without history");
|
|
1501
|
+
return [];
|
|
1502
|
+
}
|
|
1503
|
+
// Fetch replies in the thread.
|
|
1504
|
+
const repliesResponse = await API.get({
|
|
1505
|
+
url: URL.fromString(`https://graph.microsoft.com/v1.0/teams/${teamId}/channels/${channelId}/messages/${rootMessageId}/replies?$top=20`),
|
|
1506
|
+
headers: {
|
|
1507
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1508
|
+
"Content-Type": "application/json",
|
|
1509
|
+
},
|
|
1510
|
+
});
|
|
1511
|
+
if (repliesResponse instanceof HTTPErrorResponse) {
|
|
1512
|
+
logger.debug("Failed to fetch channel thread replies for AI Ops context");
|
|
1513
|
+
logger.debug(repliesResponse);
|
|
1514
|
+
}
|
|
1515
|
+
else {
|
|
1516
|
+
rawMessages =
|
|
1517
|
+
repliesResponse.data["value"] || [];
|
|
1518
|
+
}
|
|
1519
|
+
// Also fetch the root message so the original question is part of context.
|
|
1520
|
+
const rootResponse = await API.get({
|
|
1521
|
+
url: URL.fromString(`https://graph.microsoft.com/v1.0/teams/${teamId}/channels/${channelId}/messages/${rootMessageId}`),
|
|
1522
|
+
headers: {
|
|
1523
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1524
|
+
"Content-Type": "application/json",
|
|
1525
|
+
},
|
|
1526
|
+
});
|
|
1527
|
+
if (!(rootResponse instanceof HTTPErrorResponse) && rootResponse.data) {
|
|
1528
|
+
rawMessages.push(rootResponse.data);
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
// Map each Graph message to a { role, content, createdAt, id } tuple.
|
|
1532
|
+
const mappedTurns = [];
|
|
1533
|
+
for (const message of rawMessages) {
|
|
1534
|
+
const messageId = message["id"] || "";
|
|
1535
|
+
// Exclude the current triggering message - that is the live question.
|
|
1536
|
+
if (messageId && currentMessageId && messageId === currentMessageId) {
|
|
1537
|
+
continue;
|
|
1538
|
+
}
|
|
1539
|
+
const body = message["body"] || {};
|
|
1540
|
+
const rawContent = body["content"] || "";
|
|
1541
|
+
const content = this.toPlainTextFromTeamsMessageBody(rawContent);
|
|
1542
|
+
if (!content) {
|
|
1543
|
+
continue;
|
|
1544
|
+
}
|
|
1545
|
+
// Skip the bot's transient "Looking into it…" acknowledgements.
|
|
1546
|
+
if (content === this.AI_OPS_ACK_TEXT) {
|
|
1547
|
+
continue;
|
|
1548
|
+
}
|
|
1549
|
+
/*
|
|
1550
|
+
* Classify sender. Graph marks bot/app messages via from.application; a
|
|
1551
|
+
* matching application/user id to the bot id also means it is the bot.
|
|
1552
|
+
*/
|
|
1553
|
+
const fromObj = message["from"] || {};
|
|
1554
|
+
const fromApplication = fromObj["application"] || {};
|
|
1555
|
+
const fromUser = fromObj["user"] || {};
|
|
1556
|
+
const fromApplicationId = fromApplication["id"] || "";
|
|
1557
|
+
const fromUserId = fromUser["id"] || "";
|
|
1558
|
+
const isFromBot = Boolean(fromApplication["id"]) ||
|
|
1559
|
+
(botId !== "" && (fromApplicationId === botId || fromUserId === botId));
|
|
1560
|
+
const role = isFromBot ? "assistant" : "user";
|
|
1561
|
+
const createdAtRaw = message["createdDateTime"] || "";
|
|
1562
|
+
const createdAtMs = createdAtRaw
|
|
1563
|
+
? new Date(createdAtRaw).getTime()
|
|
1564
|
+
: 0;
|
|
1565
|
+
mappedTurns.push({
|
|
1566
|
+
role: role,
|
|
1567
|
+
content: content,
|
|
1568
|
+
createdAtMs: createdAtMs,
|
|
1569
|
+
id: messageId,
|
|
1570
|
+
});
|
|
1571
|
+
}
|
|
1572
|
+
// Order oldest -> newest so the assistant reads the conversation in order.
|
|
1573
|
+
mappedTurns.sort((a, b) => {
|
|
1574
|
+
return a.createdAtMs - b.createdAtMs;
|
|
1575
|
+
});
|
|
1576
|
+
// Cap to the most recent turns.
|
|
1577
|
+
const cappedTurns = mappedTurns
|
|
1578
|
+
.slice(-this.AI_OPS_MAX_HISTORY_TURNS)
|
|
1579
|
+
.map((turn) => {
|
|
1580
|
+
return { role: turn.role, content: turn.content };
|
|
1581
|
+
});
|
|
1582
|
+
return cappedTurns;
|
|
1583
|
+
}
|
|
1584
|
+
/*
|
|
1585
|
+
* AI Ops: resolve the OneUptime user for the Teams sender, build their real
|
|
1586
|
+
* permission props, ask the observability assistant, and reply in the same
|
|
1587
|
+
* conversation with the markdown answer plus a compact "Sources" footer.
|
|
1588
|
+
*/
|
|
1589
|
+
static async answerObservabilityQuestion(data) {
|
|
1590
|
+
const { activity, turnContext, projectId, question } = data;
|
|
1591
|
+
/*
|
|
1592
|
+
* Resolve the Teams user. Teams identifies the sender by their Azure AD
|
|
1593
|
+
* object id (aadObjectId), which is what WorkspaceUserAuthToken stores as
|
|
1594
|
+
* the workspaceUserId. This mirrors how handleBotInvokeActivity resolves
|
|
1595
|
+
* the acting user.
|
|
1596
|
+
*/
|
|
1597
|
+
const fromObj = activity["from"] || {};
|
|
1598
|
+
const teamsUserId = fromObj["aadObjectId"] || undefined;
|
|
1599
|
+
if (!teamsUserId) {
|
|
1600
|
+
logger.error("AAD Object ID (teamsUserId) not found in message activity from object", {
|
|
1601
|
+
projectId: projectId.toString(),
|
|
1602
|
+
});
|
|
1603
|
+
await turnContext.sendActivity("Sorry, I couldn't identify you. Please try again later.");
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1606
|
+
// Resolve the OneUptime user linked to this Teams user.
|
|
1607
|
+
let oneUptimeUserId;
|
|
1608
|
+
try {
|
|
1609
|
+
oneUptimeUserId =
|
|
1610
|
+
await MicrosoftTeamsAuthAction.getOneUptimeUserIdFromTeamsUserId({
|
|
1611
|
+
teamsUserId: teamsUserId,
|
|
1612
|
+
projectId: projectId,
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
catch (error) {
|
|
1616
|
+
logger.debug("No OneUptime user linked to Teams user; prompting to connect account", {
|
|
1617
|
+
projectId: projectId.toString(),
|
|
1618
|
+
workspaceUserId: teamsUserId,
|
|
1619
|
+
});
|
|
1620
|
+
logger.debug(error);
|
|
1621
|
+
await turnContext.sendActivity("I couldn't find your OneUptime account. Please connect your Microsoft Teams account in OneUptime User Settings before asking me questions.");
|
|
1622
|
+
return;
|
|
1623
|
+
}
|
|
1624
|
+
/*
|
|
1625
|
+
* Let the user know we're working on it. The assistant runs a bounded,
|
|
1626
|
+
* tool-grounded agent loop and can take several seconds, so send a quick
|
|
1627
|
+
* acknowledgement first.
|
|
1628
|
+
*/
|
|
1629
|
+
try {
|
|
1630
|
+
await turnContext.sendActivity(this.AI_OPS_ACK_TEXT);
|
|
1631
|
+
}
|
|
1632
|
+
catch (ackError) {
|
|
1633
|
+
// A failed acknowledgement should not stop us from answering.
|
|
1634
|
+
logger.debug("Failed to send acknowledgement activity");
|
|
1635
|
+
logger.debug(ackError);
|
|
1636
|
+
}
|
|
1637
|
+
/*
|
|
1638
|
+
* Make the assistant context-aware: gather the prior turns of this
|
|
1639
|
+
* conversation/thread and pass them as history (oldest-first, excluding the
|
|
1640
|
+
* current question). Any failure here is non-fatal - we simply proceed
|
|
1641
|
+
* statelessly so a history problem never breaks the reply.
|
|
1642
|
+
*/
|
|
1643
|
+
let history = [];
|
|
1644
|
+
try {
|
|
1645
|
+
history = await this.getConversationHistoryTurns({
|
|
1646
|
+
activity: activity,
|
|
1647
|
+
projectId: projectId,
|
|
1648
|
+
});
|
|
1649
|
+
logger.debug(`AI Ops gathered ${history.length} prior conversation turn(s) as history`, {
|
|
1650
|
+
projectId: projectId.toString(),
|
|
1651
|
+
});
|
|
1652
|
+
}
|
|
1653
|
+
catch (historyError) {
|
|
1654
|
+
logger.debug("Failed to gather AI Ops conversation history; proceeding statelessly");
|
|
1655
|
+
logger.debug(historyError);
|
|
1656
|
+
history = [];
|
|
1657
|
+
}
|
|
1658
|
+
try {
|
|
1659
|
+
// Build the user's real permission props - the assistant's tools run under these.
|
|
1660
|
+
const props = await AccessTokenService.getDatabaseCommonInteractionPropsByUserAndProject({
|
|
1661
|
+
userId: oneUptimeUserId,
|
|
1662
|
+
projectId: projectId,
|
|
1663
|
+
});
|
|
1664
|
+
/*
|
|
1665
|
+
* Loaded on demand via require(): importing the AI toolbox at module top
|
|
1666
|
+
* pulls the entire observability tool graph — and its database
|
|
1667
|
+
* infrastructure — into the core API module graph at import time, which
|
|
1668
|
+
* trips circular-dependency init-order crashes. Teams ChatOps is the only
|
|
1669
|
+
* caller, so it is resolved lazily here. A value-position dynamic
|
|
1670
|
+
* import() is avoided because it fails TS1323 under the consuming
|
|
1671
|
+
* projects' module configuration.
|
|
1672
|
+
*/
|
|
1673
|
+
/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */
|
|
1674
|
+
const ObservabilityAssistant = require("../../AI/Chat/ObservabilityAssistant").default;
|
|
1675
|
+
/* eslint-enable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */
|
|
1676
|
+
const result = await ObservabilityAssistant.answerQuestion(Object.assign(Object.assign({ projectId: projectId, userId: oneUptimeUserId, props: props, question: question }, (history.length > 0 && { history: history })), { feature: "Microsoft Teams ChatOps" }));
|
|
1677
|
+
// Build a compact "Sources" footer from the server-minted citations.
|
|
1678
|
+
let replyText = result.contentInMarkdown;
|
|
1679
|
+
if (result.citations && result.citations.length > 0) {
|
|
1680
|
+
const sourceLines = result.citations.map((citation) => {
|
|
1681
|
+
return `• ${citation.label} (${citation.rowCount} rows)`;
|
|
1682
|
+
});
|
|
1683
|
+
replyText += `\n\n**Sources**\n${sourceLines.join("\n")}`;
|
|
1684
|
+
}
|
|
1685
|
+
await turnContext.sendActivity(replyText);
|
|
1686
|
+
logger.debug("AI Ops answer sent successfully using TurnContext", {
|
|
1687
|
+
projectId: projectId.toString(),
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1690
|
+
catch (error) {
|
|
1691
|
+
logger.error("Error answering observability question via AI Ops: " + error, {
|
|
1692
|
+
projectId: projectId.toString(),
|
|
1693
|
+
});
|
|
1694
|
+
await turnContext.sendActivity("Sorry, I ran into a problem answering that question. Please try again later.");
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1362
1697
|
// Helper methods for bot commands
|
|
1363
1698
|
static getHelpMessage() {
|
|
1364
1699
|
return `Hello! I'm the OneUptime bot. I can help you with the following commands:
|
|
1365
1700
|
|
|
1366
1701
|
**Available Commands:**
|
|
1367
1702
|
- **help** - Show this help message
|
|
1703
|
+
- **ask <question>** - Ask OneUptime AI about your logs, traces, metrics, incidents and monitors
|
|
1368
1704
|
- **create incident** - Create a new incident
|
|
1369
1705
|
- **create maintenance** - Create a new scheduled maintenance event
|
|
1370
1706
|
- **show active incidents** - Display all currently active incidents
|
|
@@ -1372,7 +1708,7 @@ class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
1372
1708
|
- **show ongoing maintenance** - Display currently ongoing maintenance events
|
|
1373
1709
|
- **show active alerts** - Display all active alerts
|
|
1374
1710
|
|
|
1375
|
-
|
|
1711
|
+
You can also just ask me a question in plain language - for example, "which monitors are down right now?" - and I'll look into your observability data for you.`;
|
|
1376
1712
|
}
|
|
1377
1713
|
static async getActiveIncidentsMessage(projectId) {
|
|
1378
1714
|
var _a, _b;
|
|
@@ -2045,6 +2381,10 @@ All monitoring checks are passing normally.`;
|
|
|
2045
2381
|
title: "help",
|
|
2046
2382
|
value: "Show quick help and useful links",
|
|
2047
2383
|
},
|
|
2384
|
+
{
|
|
2385
|
+
title: "ask",
|
|
2386
|
+
value: "Ask OneUptime AI about your logs, traces, metrics, incidents and monitors",
|
|
2387
|
+
},
|
|
2048
2388
|
{
|
|
2049
2389
|
title: "create incident",
|
|
2050
2390
|
value: "Create a new incident without leaving Teams",
|
|
@@ -2412,6 +2752,17 @@ All monitoring checks are passing normally.`;
|
|
|
2412
2752
|
}
|
|
2413
2753
|
MicrosoftTeamsUtil.cachedAdapter = null;
|
|
2414
2754
|
MicrosoftTeamsUtil.WELCOME_CARD_STATE_KEY = "oneuptime.microsoftTeams.welcomeCardSent";
|
|
2755
|
+
/*
|
|
2756
|
+
* AI Ops: transient acknowledgement text the bot posts before answering. We
|
|
2757
|
+
* must skip these when reconstructing conversation history so the assistant
|
|
2758
|
+
* never treats its own "please wait" filler as a real prior turn.
|
|
2759
|
+
*/
|
|
2760
|
+
MicrosoftTeamsUtil.AI_OPS_ACK_TEXT = "Looking into it…";
|
|
2761
|
+
/*
|
|
2762
|
+
* AI Ops: cap on how many prior turns we feed the assistant as history. The
|
|
2763
|
+
* engine also clamps history internally, but we keep the payload small.
|
|
2764
|
+
*/
|
|
2765
|
+
MicrosoftTeamsUtil.AI_OPS_MAX_HISTORY_TURNS = 12;
|
|
2415
2766
|
export default MicrosoftTeamsUtil;
|
|
2416
2767
|
__decorate([
|
|
2417
2768
|
CaptureSpan(),
|
|
@@ -2617,6 +2968,18 @@ __decorate([
|
|
|
2617
2968
|
__metadata("design:paramtypes", [Object]),
|
|
2618
2969
|
__metadata("design:returntype", Promise)
|
|
2619
2970
|
], MicrosoftTeamsUtil, "handleBotMessageActivity", null);
|
|
2971
|
+
__decorate([
|
|
2972
|
+
CaptureSpan(),
|
|
2973
|
+
__metadata("design:type", Function),
|
|
2974
|
+
__metadata("design:paramtypes", [Object]),
|
|
2975
|
+
__metadata("design:returntype", Promise)
|
|
2976
|
+
], MicrosoftTeamsUtil, "getConversationHistoryTurns", null);
|
|
2977
|
+
__decorate([
|
|
2978
|
+
CaptureSpan(),
|
|
2979
|
+
__metadata("design:type", Function),
|
|
2980
|
+
__metadata("design:paramtypes", [Object]),
|
|
2981
|
+
__metadata("design:returntype", Promise)
|
|
2982
|
+
], MicrosoftTeamsUtil, "answerObservabilityQuestion", null);
|
|
2620
2983
|
__decorate([
|
|
2621
2984
|
CaptureSpan(),
|
|
2622
2985
|
__metadata("design:type", Function),
|