@oneuptime/common 11.3.25 → 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.
Files changed (189) hide show
  1. package/Models/DatabaseModels/AIConversation.ts +322 -0
  2. package/Models/DatabaseModels/AIConversationMessage.ts +495 -0
  3. package/Models/DatabaseModels/AIRun.ts +584 -0
  4. package/Models/DatabaseModels/AIRunEvent.ts +443 -0
  5. package/Models/DatabaseModels/Index.ts +8 -0
  6. package/Models/DatabaseModels/LlmLog.ts +26 -0
  7. package/Models/DatabaseModels/LlmProvider.ts +2 -2
  8. package/Server/API/AIChatAPI.ts +693 -0
  9. package/Server/API/LlmProviderAPI.ts +169 -0
  10. package/Server/API/MicrosoftTeamsAPI.ts +5 -0
  11. package/Server/API/SlackAPI.ts +663 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1783363279075-AddAIChatModels.ts +245 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1783443471795-AddLlmProviderToAIConversation.ts +36 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1783453297388-AddAIChatWriteActionsAndWidgets.ts +46 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1783461767405-MigrationName.ts +119 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  17. package/Server/Services/AIConversationMessageService.ts +39 -0
  18. package/Server/Services/AIConversationService.ts +63 -0
  19. package/Server/Services/AIRunEventService.ts +39 -0
  20. package/Server/Services/AIRunService.ts +39 -0
  21. package/Server/Services/AIService.ts +53 -31
  22. package/Server/Services/DatabaseService.ts +19 -0
  23. package/Server/Services/LlmProviderService.ts +110 -0
  24. package/Server/Services/ProjectService.ts +24 -0
  25. package/Server/Types/AnalyticsDatabase/ModelPermission.ts +74 -9
  26. package/Server/Utils/AI/AIChatPrivacyFilter.ts +28 -0
  27. package/Server/Utils/AI/Chat/ChatAgentRunner.ts +1054 -0
  28. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +239 -0
  29. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +51 -0
  30. package/Server/Utils/AI/Toolbox/AlertTools.ts +201 -0
  31. package/Server/Utils/AI/Toolbox/AlertWriteTools.ts +174 -0
  32. package/Server/Utils/AI/Toolbox/ContextTools.ts +189 -0
  33. package/Server/Utils/AI/Toolbox/ExceptionTools.ts +149 -0
  34. package/Server/Utils/AI/Toolbox/IncidentTools.ts +204 -0
  35. package/Server/Utils/AI/Toolbox/IncidentWriteTools.ts +350 -0
  36. package/Server/Utils/AI/Toolbox/Index.ts +228 -0
  37. package/Server/Utils/AI/Toolbox/LogTools.ts +339 -0
  38. package/Server/Utils/AI/Toolbox/MetricTools.ts +179 -0
  39. package/Server/Utils/AI/Toolbox/MonitorTools.ts +193 -0
  40. package/Server/Utils/AI/Toolbox/Serializer.ts +257 -0
  41. package/Server/Utils/AI/Toolbox/ToolTypes.ts +249 -0
  42. package/Server/Utils/AI/Toolbox/TraceTools.ts +402 -0
  43. package/Server/Utils/AI/Toolbox/WidgetBuilder.ts +189 -0
  44. package/Server/Utils/LLM/LLMService.ts +502 -92
  45. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +512 -19
  46. package/Server/Utils/Workspace/Slack/Slack.ts +80 -0
  47. package/Server/Utils/Workspace/Slack/app-manifest.json +16 -1
  48. package/Tests/Server/Utils/AI/AIChatModelACL.test.ts +42 -0
  49. package/Tests/Server/Utils/AI/ChatAgentHelpers.test.ts +89 -0
  50. package/Tests/Server/Utils/AI/LLMServiceBaseUrl.test.ts +139 -0
  51. package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +308 -0
  52. package/Tests/Server/Utils/AI/ToolArgsGetTimeRange.test.ts +62 -0
  53. package/Tests/Server/Utils/AI/ToolArgsScopeServiceIds.test.ts +79 -0
  54. package/Tests/Server/Utils/AI/ToolResultSerializer.test.ts +155 -0
  55. package/Tests/Types/Billing/SubscriptionStatus.test.ts +127 -0
  56. package/Tests/Types/Metrics/RecordingRuleDefinition.test.ts +213 -0
  57. package/Types/AI/AIChatMessageRole.ts +6 -0
  58. package/Types/AI/AIChatMessageStatus.ts +32 -0
  59. package/Types/AI/AIChatPermissionMode.ts +69 -0
  60. package/Types/AI/AIChatTypes.ts +231 -0
  61. package/Types/AI/AIRunEventType.ts +17 -0
  62. package/Types/AI/AIRunStatus.ts +28 -0
  63. package/Types/AI/AIRunType.ts +6 -0
  64. package/Types/LLM/LlmType.ts +6 -0
  65. package/UI/Components/Header/HeaderIconDropdownButton.tsx +22 -12
  66. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +35 -3
  67. package/UI/Components/Page/Page.tsx +14 -0
  68. package/UI/Utils/LlmTypeDropdownOptions.ts +40 -0
  69. package/UI/Utils/TestLLMProvider.ts +59 -0
  70. package/build/dist/Models/DatabaseModels/AIConversation.js +345 -0
  71. package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -0
  72. package/build/dist/Models/DatabaseModels/AIConversationMessage.js +521 -0
  73. package/build/dist/Models/DatabaseModels/AIConversationMessage.js.map +1 -0
  74. package/build/dist/Models/DatabaseModels/AIRun.js +619 -0
  75. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -0
  76. package/build/dist/Models/DatabaseModels/AIRunEvent.js +469 -0
  77. package/build/dist/Models/DatabaseModels/AIRunEvent.js.map +1 -0
  78. package/build/dist/Models/DatabaseModels/Index.js +8 -0
  79. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  80. package/build/dist/Models/DatabaseModels/LlmLog.js +28 -0
  81. package/build/dist/Models/DatabaseModels/LlmLog.js.map +1 -1
  82. package/build/dist/Models/DatabaseModels/LlmProvider.js +2 -2
  83. package/build/dist/Models/DatabaseModels/LlmProvider.js.map +1 -1
  84. package/build/dist/Server/API/AIChatAPI.js +498 -0
  85. package/build/dist/Server/API/AIChatAPI.js.map +1 -0
  86. package/build/dist/Server/API/LlmProviderAPI.js +107 -1
  87. package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
  88. package/build/dist/Server/API/MicrosoftTeamsAPI.js +4 -0
  89. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  90. package/build/dist/Server/API/SlackAPI.js +442 -0
  91. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  92. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783363279075-AddAIChatModels.js +96 -0
  93. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783363279075-AddAIChatModels.js.map +1 -0
  94. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783443471795-AddLlmProviderToAIConversation.js +25 -0
  95. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783443471795-AddLlmProviderToAIConversation.js.map +1 -0
  96. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783453297388-AddAIChatWriteActionsAndWidgets.js +31 -0
  97. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783453297388-AddAIChatWriteActionsAndWidgets.js.map +1 -0
  98. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783461767405-MigrationName.js +46 -0
  99. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783461767405-MigrationName.js.map +1 -0
  100. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  101. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  102. package/build/dist/Server/Services/AIConversationMessageService.js +34 -0
  103. package/build/dist/Server/Services/AIConversationMessageService.js.map +1 -0
  104. package/build/dist/Server/Services/AIConversationService.js +42 -0
  105. package/build/dist/Server/Services/AIConversationService.js.map +1 -0
  106. package/build/dist/Server/Services/AIRunEventService.js +34 -0
  107. package/build/dist/Server/Services/AIRunEventService.js.map +1 -0
  108. package/build/dist/Server/Services/AIRunService.js +34 -0
  109. package/build/dist/Server/Services/AIRunService.js.map +1 -0
  110. package/build/dist/Server/Services/AIService.js +31 -23
  111. package/build/dist/Server/Services/AIService.js.map +1 -1
  112. package/build/dist/Server/Services/DatabaseService.js +10 -0
  113. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  114. package/build/dist/Server/Services/LlmProviderService.js +108 -0
  115. package/build/dist/Server/Services/LlmProviderService.js.map +1 -1
  116. package/build/dist/Server/Services/ProjectService.js +25 -0
  117. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  118. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +50 -7
  119. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
  120. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js +18 -0
  121. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js.map +1 -0
  122. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +745 -0
  123. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -0
  124. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +165 -0
  125. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -0
  126. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +44 -0
  127. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -0
  128. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +167 -0
  129. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -0
  130. package/build/dist/Server/Utils/AI/Toolbox/AlertWriteTools.js +136 -0
  131. package/build/dist/Server/Utils/AI/Toolbox/AlertWriteTools.js.map +1 -0
  132. package/build/dist/Server/Utils/AI/Toolbox/ContextTools.js +141 -0
  133. package/build/dist/Server/Utils/AI/Toolbox/ContextTools.js.map +1 -0
  134. package/build/dist/Server/Utils/AI/Toolbox/ExceptionTools.js +117 -0
  135. package/build/dist/Server/Utils/AI/Toolbox/ExceptionTools.js.map +1 -0
  136. package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js +167 -0
  137. package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -0
  138. package/build/dist/Server/Utils/AI/Toolbox/IncidentWriteTools.js +280 -0
  139. package/build/dist/Server/Utils/AI/Toolbox/IncidentWriteTools.js.map +1 -0
  140. package/build/dist/Server/Utils/AI/Toolbox/Index.js +150 -0
  141. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -0
  142. package/build/dist/Server/Utils/AI/Toolbox/LogTools.js +246 -0
  143. package/build/dist/Server/Utils/AI/Toolbox/LogTools.js.map +1 -0
  144. package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js +120 -0
  145. package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js.map +1 -0
  146. package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js +158 -0
  147. package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js.map +1 -0
  148. package/build/dist/Server/Utils/AI/Toolbox/Serializer.js +188 -0
  149. package/build/dist/Server/Utils/AI/Toolbox/Serializer.js.map +1 -0
  150. package/build/dist/Server/Utils/AI/Toolbox/ToolTypes.js +142 -0
  151. package/build/dist/Server/Utils/AI/Toolbox/ToolTypes.js.map +1 -0
  152. package/build/dist/Server/Utils/AI/Toolbox/TraceTools.js +309 -0
  153. package/build/dist/Server/Utils/AI/Toolbox/TraceTools.js.map +1 -0
  154. package/build/dist/Server/Utils/AI/Toolbox/WidgetBuilder.js +120 -0
  155. package/build/dist/Server/Utils/AI/Toolbox/WidgetBuilder.js.map +1 -0
  156. package/build/dist/Server/Utils/LLM/LLMService.js +372 -88
  157. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  158. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +378 -15
  159. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  160. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +59 -0
  161. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  162. package/build/dist/Server/Utils/Workspace/Slack/app-manifest.json +16 -1
  163. package/build/dist/Types/AI/AIChatMessageRole.js +7 -0
  164. package/build/dist/Types/AI/AIChatMessageRole.js.map +1 -0
  165. package/build/dist/Types/AI/AIChatMessageStatus.js +27 -0
  166. package/build/dist/Types/AI/AIChatMessageStatus.js.map +1 -0
  167. package/build/dist/Types/AI/AIChatPermissionMode.js +53 -0
  168. package/build/dist/Types/AI/AIChatPermissionMode.js.map +1 -0
  169. package/build/dist/Types/AI/AIChatTypes.js +74 -0
  170. package/build/dist/Types/AI/AIChatTypes.js.map +1 -0
  171. package/build/dist/Types/AI/AIRunEventType.js +18 -0
  172. package/build/dist/Types/AI/AIRunEventType.js.map +1 -0
  173. package/build/dist/Types/AI/AIRunStatus.js +26 -0
  174. package/build/dist/Types/AI/AIRunStatus.js.map +1 -0
  175. package/build/dist/Types/AI/AIRunType.js +7 -0
  176. package/build/dist/Types/AI/AIRunType.js.map +1 -0
  177. package/build/dist/Types/LLM/LlmType.js +6 -0
  178. package/build/dist/Types/LLM/LlmType.js.map +1 -1
  179. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +10 -5
  180. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  181. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +18 -4
  182. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  183. package/build/dist/UI/Components/Page/Page.js +3 -1
  184. package/build/dist/UI/Components/Page/Page.js.map +1 -1
  185. package/build/dist/UI/Utils/LlmTypeDropdownOptions.js +38 -0
  186. package/build/dist/UI/Utils/LlmTypeDropdownOptions.js.map +1 -0
  187. package/build/dist/UI/Utils/TestLLMProvider.js +37 -0
  188. package/build/dist/UI/Utils/TestLLMProvider.js.map +1 -0
  189. package/package.json +1 -1
@@ -0,0 +1,189 @@
1
+ import MetricType from "../../../../Models/DatabaseModels/MetricType";
2
+ import Service from "../../../../Models/DatabaseModels/Service";
3
+ import SortOrder from "../../../../Types/BaseDatabase/SortOrder";
4
+ import BadDataException from "../../../../Types/Exception/BadDataException";
5
+ import { JSONObject } from "../../../../Types/JSON";
6
+ import Permission from "../../../../Types/Permission";
7
+ import TelemetryType from "../../../../Types/Telemetry/TelemetryType";
8
+ import MetricTypeService from "../../../Services/MetricTypeService";
9
+ import ServiceService from "../../../Services/ServiceService";
10
+ import TelemetryAttributeService from "../../../Services/TelemetryAttributeService";
11
+ import QueryHelper from "../../../Types/Database/QueryHelper";
12
+ import ToolResultSerializer, { SerializedResult } from "./Serializer";
13
+ import {
14
+ ObservabilityTool,
15
+ ToolArgs,
16
+ ToolContext,
17
+ ToolExecutionResult,
18
+ } from "./ToolTypes";
19
+
20
+ const CONTEXT_READ_PERMISSIONS: Array<Permission> = [
21
+ Permission.ProjectOwner,
22
+ Permission.ProjectAdmin,
23
+ Permission.ProjectMember,
24
+ Permission.Viewer,
25
+ Permission.TelemetryAdmin,
26
+ Permission.TelemetryMember,
27
+ Permission.TelemetryViewer,
28
+ ];
29
+
30
+ export const LookupContextTool: ObservabilityTool = {
31
+ name: "lookup_context",
32
+ description:
33
+ "Discover what exists in this project so other tools can be called with exact identifiers: telemetry services (name → ID), metric names, and attribute keys for logs/traces/metrics. ALWAYS resolve a service name to its ID here before filtering other tools by service.",
34
+ inputSchema: {
35
+ type: "object",
36
+ properties: {
37
+ type: {
38
+ type: "string",
39
+ enum: ["services", "metricNames", "attributeKeys"],
40
+ description: "What to look up (required).",
41
+ },
42
+ nameSearch: {
43
+ type: "string",
44
+ description: "Filter results whose name contains this text.",
45
+ },
46
+ telemetryType: {
47
+ type: "string",
48
+ enum: ["Log", "Trace", "Metric", "Exception"],
49
+ description:
50
+ "For attributeKeys lookups: which signal's attribute keys to list.",
51
+ },
52
+ },
53
+ required: ["type"],
54
+ },
55
+ requiredPermissions: CONTEXT_READ_PERMISSIONS,
56
+ execute: async (
57
+ args: JSONObject,
58
+ ctx: ToolContext,
59
+ ): Promise<ToolExecutionResult> => {
60
+ const lookupType: string | undefined = ToolArgs.getString(args, "type");
61
+ const nameSearch: string | undefined = ToolArgs.getString(
62
+ args,
63
+ "nameSearch",
64
+ );
65
+
66
+ if (lookupType === "services") {
67
+ const services: Array<Service> = await ServiceService.findBy({
68
+ query: nameSearch
69
+ ? {
70
+ name: QueryHelper.search(nameSearch),
71
+ }
72
+ : {},
73
+ select: {
74
+ _id: true,
75
+ name: true,
76
+ },
77
+ sort: {
78
+ name: SortOrder.Ascending,
79
+ },
80
+ limit: 100,
81
+ skip: 0,
82
+ props: ctx.props,
83
+ });
84
+
85
+ const rows: Array<JSONObject> = services.map((service: Service) => {
86
+ return {
87
+ id: service.id?.toString(),
88
+ name: service.name,
89
+ };
90
+ });
91
+
92
+ const serialized: SerializedResult =
93
+ ToolResultSerializer.serializeRows(rows);
94
+
95
+ return {
96
+ dataForLlm: serialized.text,
97
+ rowCount: serialized.rowCount,
98
+ citationLabel: `Telemetry services (${serialized.rowCount} found)`,
99
+ citationTarget: undefined,
100
+ redactionCount: serialized.redactionCount,
101
+ isTruncated: serialized.isTruncated,
102
+ };
103
+ }
104
+
105
+ if (lookupType === "metricNames") {
106
+ const metricTypes: Array<MetricType> = await MetricTypeService.findBy({
107
+ query: nameSearch
108
+ ? {
109
+ name: QueryHelper.search(nameSearch),
110
+ }
111
+ : {},
112
+ select: {
113
+ name: true,
114
+ },
115
+ sort: {
116
+ name: SortOrder.Ascending,
117
+ },
118
+ limit: 200,
119
+ skip: 0,
120
+ props: ctx.props,
121
+ });
122
+
123
+ const rows: Array<JSONObject> = metricTypes.map(
124
+ (metricType: MetricType) => {
125
+ return {
126
+ name: metricType.name,
127
+ };
128
+ },
129
+ );
130
+
131
+ const serialized: SerializedResult =
132
+ ToolResultSerializer.serializeRows(rows);
133
+
134
+ return {
135
+ dataForLlm: serialized.text,
136
+ rowCount: serialized.rowCount,
137
+ citationLabel: `Metric names (${serialized.rowCount} found)`,
138
+ citationTarget: undefined,
139
+ redactionCount: serialized.redactionCount,
140
+ isTruncated: serialized.isTruncated,
141
+ };
142
+ }
143
+
144
+ if (lookupType === "attributeKeys") {
145
+ const telemetryTypeString: string =
146
+ ToolArgs.getString(args, "telemetryType") || "Log";
147
+
148
+ if (
149
+ !Object.values(TelemetryType).includes(
150
+ telemetryTypeString as TelemetryType,
151
+ )
152
+ ) {
153
+ throw new BadDataException(
154
+ `Invalid telemetryType: ${telemetryTypeString}`,
155
+ );
156
+ }
157
+
158
+ const attributes: Array<string> =
159
+ await TelemetryAttributeService.fetchAttributes({
160
+ projectId: ctx.projectId,
161
+ telemetryType: telemetryTypeString as TelemetryType,
162
+ });
163
+
164
+ const filtered: Array<string> = nameSearch
165
+ ? attributes.filter((attribute: string) => {
166
+ return attribute.toLowerCase().includes(nameSearch.toLowerCase());
167
+ })
168
+ : attributes;
169
+
170
+ const serialized: SerializedResult = ToolResultSerializer.serializeText(
171
+ filtered.slice(0, 200).join("\n"),
172
+ filtered.length,
173
+ );
174
+
175
+ return {
176
+ dataForLlm: serialized.text,
177
+ rowCount: serialized.rowCount,
178
+ citationLabel: `${telemetryTypeString} attribute keys (${filtered.length} found)`,
179
+ citationTarget: undefined,
180
+ redactionCount: serialized.redactionCount,
181
+ isTruncated: serialized.isTruncated,
182
+ };
183
+ }
184
+
185
+ throw new BadDataException(
186
+ `Invalid lookup type: ${lookupType}. Use one of: services, metricNames, attributeKeys.`,
187
+ );
188
+ },
189
+ };
@@ -0,0 +1,149 @@
1
+ import TelemetryException from "../../../../Models/DatabaseModels/TelemetryException";
2
+ import { JSONObject } from "../../../../Types/JSON";
3
+ import Permission from "../../../../Types/Permission";
4
+ import SortOrder from "../../../../Types/BaseDatabase/SortOrder";
5
+ import { AIChatCitationTargetType } from "../../../../Types/AI/AIChatTypes";
6
+ import TelemetryExceptionService from "../../../Services/TelemetryExceptionService";
7
+ import QueryHelper from "../../../Types/Database/QueryHelper";
8
+ import OneUptimeDate from "../../../../Types/Date";
9
+ import ToolResultSerializer, { SerializedResult } from "./Serializer";
10
+ import WidgetBuilder from "./WidgetBuilder";
11
+ import {
12
+ ObservabilityTool,
13
+ ToolArgs,
14
+ ToolContext,
15
+ ToolExecutionResult,
16
+ } from "./ToolTypes";
17
+
18
+ /*
19
+ * Derived from the model ACL so the tool gate can never drift from RBAC.
20
+ * Resolved lazily rather than at module load: this module is pulled in through
21
+ * the service import graph before the TelemetryException model class is fully
22
+ * wired up, so calling a model method at import time throws a
23
+ * circular-dependency TypeError. By the time a tool actually executes, every
24
+ * module is loaded.
25
+ */
26
+ let cachedReadPermissions: Array<Permission> | null = null;
27
+ const resolveReadPermissions: () => Array<Permission> =
28
+ (): Array<Permission> => {
29
+ if (!cachedReadPermissions) {
30
+ cachedReadPermissions = new TelemetryException().getReadPermissions();
31
+ }
32
+ return cachedReadPermissions;
33
+ };
34
+
35
+ export const TopExceptionsTool: ObservabilityTool = {
36
+ name: "top_exceptions",
37
+ description:
38
+ "Get the top exceptions (grouped by fingerprint) in this project, sorted by occurrence count. Includes first/last seen timestamps so you can tell if an exception is new or recurring.",
39
+ inputSchema: {
40
+ type: "object",
41
+ properties: {
42
+ lastSeenWithinHours: {
43
+ type: "number",
44
+ description:
45
+ "Only exceptions last seen within this many hours (default 168 = 7 days, max 720).",
46
+ },
47
+ includeResolved: {
48
+ type: "boolean",
49
+ description: "Include resolved exceptions (default false).",
50
+ },
51
+ limit: {
52
+ type: "number",
53
+ description: "Maximum exception groups to return (default 10, max 25).",
54
+ },
55
+ },
56
+ },
57
+ get requiredPermissions(): Array<Permission> {
58
+ return resolveReadPermissions();
59
+ },
60
+ execute: async (
61
+ args: JSONObject,
62
+ ctx: ToolContext,
63
+ ): Promise<ToolExecutionResult> => {
64
+ const lastSeenWithinHours: number = ToolArgs.getNumber(
65
+ args,
66
+ "lastSeenWithinHours",
67
+ { defaultValue: 168, min: 1, max: 720 },
68
+ );
69
+ const includeResolved: boolean =
70
+ ToolArgs.getBoolean(args, "includeResolved") ?? false;
71
+ const limit: number = ToolArgs.getNumber(args, "limit", {
72
+ defaultValue: 10,
73
+ min: 1,
74
+ max: 25,
75
+ });
76
+
77
+ const since: Date = OneUptimeDate.addRemoveHours(
78
+ OneUptimeDate.getCurrentDate(),
79
+ -1 * lastSeenWithinHours,
80
+ );
81
+
82
+ const query: JSONObject = {
83
+ lastSeenAt: QueryHelper.greaterThanEqualTo(since),
84
+ };
85
+
86
+ if (!includeResolved) {
87
+ query["isResolved"] = false;
88
+ }
89
+
90
+ const exceptions: Array<TelemetryException> =
91
+ await TelemetryExceptionService.findBy({
92
+ query: query as never,
93
+ select: {
94
+ _id: true,
95
+ message: true,
96
+ exceptionType: true,
97
+ fingerprint: true,
98
+ occuranceCount: true,
99
+ firstSeenAt: true,
100
+ lastSeenAt: true,
101
+ isResolved: true,
102
+ },
103
+ sort: {
104
+ occuranceCount: SortOrder.Descending,
105
+ },
106
+ limit: limit,
107
+ skip: 0,
108
+ props: ctx.props,
109
+ });
110
+
111
+ const rows: Array<JSONObject> = exceptions.map(
112
+ (exception: TelemetryException) => {
113
+ return {
114
+ id: exception.id?.toString(),
115
+ type: exception.exceptionType,
116
+ message: exception.message,
117
+ occurrences: exception.occuranceCount,
118
+ firstSeenAt: exception.firstSeenAt,
119
+ lastSeenAt: exception.lastSeenAt,
120
+ isResolved: exception.isResolved,
121
+ fingerprint: exception.fingerprint,
122
+ };
123
+ },
124
+ );
125
+
126
+ const serialized: SerializedResult =
127
+ ToolResultSerializer.serializeRows(rows);
128
+
129
+ return {
130
+ dataForLlm: serialized.text,
131
+ rowCount: serialized.rowCount,
132
+ citationLabel: `Top exceptions, last ${lastSeenWithinHours}h (${serialized.rowCount} found)`,
133
+ citationTarget: {
134
+ type: AIChatCitationTargetType.Exceptions,
135
+ },
136
+ redactionCount: serialized.redactionCount,
137
+ isTruncated: serialized.isTruncated,
138
+ widget:
139
+ rows.length > 0
140
+ ? WidgetBuilder.exceptionList({
141
+ title: `Top exceptions (${rows.length})`,
142
+ description: `Last ${lastSeenWithinHours}h, by occurrence count`,
143
+ items: rows,
144
+ link: { type: AIChatCitationTargetType.Exceptions },
145
+ })
146
+ : undefined,
147
+ };
148
+ },
149
+ };
@@ -0,0 +1,204 @@
1
+ import Incident from "../../../../Models/DatabaseModels/Incident";
2
+ import { JSONObject } from "../../../../Types/JSON";
3
+ import ObjectID from "../../../../Types/ObjectID";
4
+ import Permission from "../../../../Types/Permission";
5
+ import SortOrder from "../../../../Types/BaseDatabase/SortOrder";
6
+ import { AIChatCitationTargetType } from "../../../../Types/AI/AIChatTypes";
7
+ import IncidentService from "../../../Services/IncidentService";
8
+ import QueryHelper from "../../../Types/Database/QueryHelper";
9
+ import OneUptimeDate from "../../../../Types/Date";
10
+ import ToolResultSerializer, { SerializedResult } from "./Serializer";
11
+ import WidgetBuilder from "./WidgetBuilder";
12
+ import {
13
+ ObservabilityTool,
14
+ ToolArgs,
15
+ ToolContext,
16
+ ToolExecutionResult,
17
+ } from "./ToolTypes";
18
+
19
+ /*
20
+ * Derived from the model ACL so the tool gate can never drift from RBAC.
21
+ * Resolved lazily rather than at module load: this module is pulled in through
22
+ * the service import graph before the Incident model class is fully wired up,
23
+ * so calling a model method at import time throws a circular-dependency
24
+ * TypeError. By the time a tool actually executes, every module is loaded.
25
+ */
26
+ let cachedReadPermissions: Array<Permission> | null = null;
27
+ const resolveReadPermissions: () => Array<Permission> =
28
+ (): Array<Permission> => {
29
+ if (!cachedReadPermissions) {
30
+ cachedReadPermissions = new Incident().getReadPermissions();
31
+ }
32
+ return cachedReadPermissions;
33
+ };
34
+
35
+ export const QueryIncidentsTool: ObservabilityTool = {
36
+ name: "query_incidents",
37
+ description:
38
+ "Query incidents in this project. Returns the most recent incidents with their current state and severity. Pass incidentId to get full details of one incident.",
39
+ inputSchema: {
40
+ type: "object",
41
+ properties: {
42
+ incidentId: {
43
+ type: "string",
44
+ description: "Get one incident by its ID (includes description).",
45
+ },
46
+ createdWithinHours: {
47
+ type: "number",
48
+ description:
49
+ "Only incidents created within this many hours (default 168 = 7 days, max 720).",
50
+ },
51
+ limit: {
52
+ type: "number",
53
+ description: "Maximum incidents to return (default 10, max 25).",
54
+ },
55
+ },
56
+ },
57
+ get requiredPermissions(): Array<Permission> {
58
+ return resolveReadPermissions();
59
+ },
60
+ execute: async (
61
+ args: JSONObject,
62
+ ctx: ToolContext,
63
+ ): Promise<ToolExecutionResult> => {
64
+ const incidentId: ObjectID | undefined = ToolArgs.getObjectID(
65
+ args,
66
+ "incidentId",
67
+ );
68
+
69
+ if (incidentId) {
70
+ const incident: Incident | null = await IncidentService.findOneById({
71
+ id: incidentId,
72
+ select: {
73
+ _id: true,
74
+ title: true,
75
+ description: true,
76
+ incidentNumber: true,
77
+ createdAt: true,
78
+ currentIncidentState: {
79
+ name: true,
80
+ },
81
+ incidentSeverity: {
82
+ name: true,
83
+ },
84
+ },
85
+ props: ctx.props,
86
+ });
87
+
88
+ const rows: Array<JSONObject> = incident
89
+ ? [
90
+ {
91
+ id: incident.id?.toString(),
92
+ incidentNumber: incident.incidentNumber,
93
+ title: incident.title,
94
+ description: incident.description,
95
+ state: incident.currentIncidentState?.name,
96
+ severity: incident.incidentSeverity?.name,
97
+ createdAt: incident.createdAt,
98
+ },
99
+ ]
100
+ : [];
101
+
102
+ const serialized: SerializedResult =
103
+ ToolResultSerializer.serializeRows(rows);
104
+
105
+ return {
106
+ dataForLlm: serialized.text,
107
+ rowCount: serialized.rowCount,
108
+ citationLabel: `Incident ${incident?.incidentNumber ? `#${incident.incidentNumber}` : incidentId.toString()}`,
109
+ citationTarget: {
110
+ type: AIChatCitationTargetType.IncidentView,
111
+ params: { incidentId: incidentId.toString() },
112
+ },
113
+ redactionCount: serialized.redactionCount,
114
+ isTruncated: serialized.isTruncated,
115
+ widget:
116
+ rows.length > 0
117
+ ? WidgetBuilder.incidentList({
118
+ title: `Incident #${incident?.incidentNumber ?? ""}`.trim(),
119
+ items: rows,
120
+ link: {
121
+ type: AIChatCitationTargetType.IncidentView,
122
+ params: { incidentId: incidentId.toString() },
123
+ },
124
+ })
125
+ : undefined,
126
+ };
127
+ }
128
+
129
+ const createdWithinHours: number = ToolArgs.getNumber(
130
+ args,
131
+ "createdWithinHours",
132
+ { defaultValue: 168, min: 1, max: 720 },
133
+ );
134
+ const limit: number = ToolArgs.getNumber(args, "limit", {
135
+ defaultValue: 10,
136
+ min: 1,
137
+ max: 25,
138
+ });
139
+
140
+ const endTime: Date = OneUptimeDate.getCurrentDate();
141
+ const startTime: Date = OneUptimeDate.addRemoveHours(
142
+ endTime,
143
+ -1 * createdWithinHours,
144
+ );
145
+
146
+ const incidents: Array<Incident> = await IncidentService.findBy({
147
+ query: {
148
+ createdAt: QueryHelper.inBetween(startTime, endTime),
149
+ },
150
+ select: {
151
+ _id: true,
152
+ title: true,
153
+ incidentNumber: true,
154
+ createdAt: true,
155
+ currentIncidentState: {
156
+ name: true,
157
+ },
158
+ incidentSeverity: {
159
+ name: true,
160
+ },
161
+ },
162
+ sort: {
163
+ createdAt: SortOrder.Descending,
164
+ },
165
+ limit: limit,
166
+ skip: 0,
167
+ props: ctx.props,
168
+ });
169
+
170
+ const rows: Array<JSONObject> = incidents.map((incident: Incident) => {
171
+ return {
172
+ id: incident.id?.toString(),
173
+ incidentNumber: incident.incidentNumber,
174
+ title: incident.title,
175
+ state: incident.currentIncidentState?.name,
176
+ severity: incident.incidentSeverity?.name,
177
+ createdAt: incident.createdAt,
178
+ };
179
+ });
180
+
181
+ const serialized: SerializedResult =
182
+ ToolResultSerializer.serializeRows(rows);
183
+
184
+ return {
185
+ dataForLlm: serialized.text,
186
+ rowCount: serialized.rowCount,
187
+ citationLabel: `Incidents, last ${createdWithinHours}h (${serialized.rowCount} found)`,
188
+ citationTarget: {
189
+ type: AIChatCitationTargetType.Incidents,
190
+ },
191
+ redactionCount: serialized.redactionCount,
192
+ isTruncated: serialized.isTruncated,
193
+ widget:
194
+ rows.length > 0
195
+ ? WidgetBuilder.incidentList({
196
+ title: `Incidents (${rows.length})`,
197
+ description: `Created in the last ${createdWithinHours}h`,
198
+ items: rows,
199
+ link: { type: AIChatCitationTargetType.Incidents },
200
+ })
201
+ : undefined,
202
+ };
203
+ },
204
+ };