@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,188 @@
1
+ /*
2
+ * Prepares tool results for the LLM prompt: redacts likely secrets/PII,
3
+ * truncates long fields, and caps total payload size. Everything that enters
4
+ * the prompt (and therefore leaves for the LLM provider, and is previewed in
5
+ * LlmLog.requestPrompt) passes through here first.
6
+ */
7
+ const MAX_ROWS = 50;
8
+ const MAX_FIELD_LENGTH = 500;
9
+ const MAX_PAYLOAD_BYTES = 16 * 1024;
10
+ /*
11
+ * These are security redactions, not the fingerprint-normalization regexes
12
+ * (those normalize numbers/uuids for grouping and do not cover credentials).
13
+ */
14
+ const REDACTION_RULES = [
15
+ {
16
+ name: "jwt",
17
+ regex: /eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{5,}/g,
18
+ replacement: "[redacted-jwt]",
19
+ },
20
+ {
21
+ name: "bearer",
22
+ regex: /Bearer\s+[A-Za-z0-9._~+/-]{16,}=*/gi,
23
+ replacement: "Bearer [redacted-token]",
24
+ },
25
+ {
26
+ name: "email",
27
+ regex: /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g,
28
+ replacement: "[redacted-email]",
29
+ },
30
+ {
31
+ name: "ipv4",
32
+ regex: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g,
33
+ replacement: "[redacted-ip]",
34
+ },
35
+ {
36
+ /*
37
+ * Long hex blobs are usually secrets (SHA-256 digests, 40+ char API
38
+ * keys). But W3C/OTel trace IDs are exactly 32 hex chars and span IDs
39
+ * exactly 16 — redacting those would break the documented
40
+ * search_logs → get_trace pivot, since the model would never see the ID
41
+ * it needs to pass back. So only redact runs of 33+ hex chars, which
42
+ * preserves both ID lengths while still catching typical hash-length
43
+ * secrets. Labeled secrets of any length are still caught by
44
+ * key-value-secret below.
45
+ */
46
+ name: "hex-secret",
47
+ regex: /\b[0-9a-fA-F]{33,}\b/g,
48
+ replacement: "[redacted-hex]",
49
+ },
50
+ {
51
+ name: "key-value-secret",
52
+ regex: /\b(password|passwd|secret|api[_-]?key|access[_-]?token|authorization)\b(\s*[:=]\s*)("[^"]{4,}"|'[^']{4,}'|[^\s,;&]{4,})/gi,
53
+ replacement: "$1$2[redacted]",
54
+ },
55
+ ];
56
+ export default class ToolResultSerializer {
57
+ // Single pass per rule: count and substitute ($1/$2 groups) together.
58
+ static applyRedactionRule(text, rule) {
59
+ let count = 0;
60
+ const result = text.replace(rule.regex, (...matchArgs) => {
61
+ count++;
62
+ return rule.replacement.replace(/\$(\d)/g, (_full, groupIndex) => {
63
+ var _a;
64
+ return String((_a = matchArgs[Number(groupIndex)]) !== null && _a !== void 0 ? _a : "");
65
+ });
66
+ });
67
+ return { text: result, count };
68
+ }
69
+ static redact(text) {
70
+ let redacted = text;
71
+ let count = 0;
72
+ for (const rule of REDACTION_RULES) {
73
+ const applied = this.applyRedactionRule(redacted, rule);
74
+ redacted = applied.text;
75
+ count += applied.count;
76
+ }
77
+ return { text: redacted, count };
78
+ }
79
+ // Byte-accurate truncation (substring counts UTF-16 units, not bytes).
80
+ static truncateToBytes(text, maxBytes) {
81
+ if (Buffer.byteLength(text, "utf8") <= maxBytes) {
82
+ return text;
83
+ }
84
+ return Buffer.from(text, "utf8").subarray(0, maxBytes).toString("utf8");
85
+ }
86
+ static truncateField(value) {
87
+ if (value.length <= MAX_FIELD_LENGTH) {
88
+ return { value, truncated: false };
89
+ }
90
+ return {
91
+ value: `${value.substring(0, MAX_FIELD_LENGTH)}… [truncated]`,
92
+ truncated: true,
93
+ };
94
+ }
95
+ static serializeValue(value) {
96
+ if (value === null || value === undefined) {
97
+ return "";
98
+ }
99
+ if (value instanceof Date) {
100
+ return value.toISOString();
101
+ }
102
+ if (typeof value === "object") {
103
+ return JSON.stringify(value);
104
+ }
105
+ return String(value);
106
+ }
107
+ /*
108
+ * Serialize rows as a compact markdown-ish record list. Applies row cap,
109
+ * per-field truncation, redaction and total payload cap.
110
+ */
111
+ static serializeRows(rows, totalRowCount) {
112
+ let isTruncated = false;
113
+ let redactionCount = 0;
114
+ let rowsToSerialize = rows;
115
+ if (rows.length > MAX_ROWS) {
116
+ rowsToSerialize = rows.slice(0, MAX_ROWS);
117
+ isTruncated = true;
118
+ }
119
+ const lines = [];
120
+ for (const row of rowsToSerialize) {
121
+ const parts = [];
122
+ for (const key of Object.keys(row)) {
123
+ const rawValue = this.serializeValue(row[key]);
124
+ if (!rawValue) {
125
+ continue;
126
+ }
127
+ const redacted = this.redact(rawValue);
128
+ redactionCount += redacted.count;
129
+ const truncated = this.truncateField(redacted.text);
130
+ if (truncated.truncated) {
131
+ isTruncated = true;
132
+ }
133
+ parts.push(`${key}=${truncated.value}`);
134
+ }
135
+ lines.push(`- ${parts.join(" | ")}`);
136
+ }
137
+ let text = lines.join("\n");
138
+ if (Buffer.byteLength(text, "utf8") > MAX_PAYLOAD_BYTES) {
139
+ isTruncated = true;
140
+ while (lines.length > 1 &&
141
+ Buffer.byteLength(lines.join("\n"), "utf8") > MAX_PAYLOAD_BYTES) {
142
+ lines.pop();
143
+ }
144
+ // A single oversized row can still exceed the cap — hard-slice it.
145
+ text = `${this.truncateToBytes(lines.join("\n"), MAX_PAYLOAD_BYTES)}\n… [payload truncated]`;
146
+ }
147
+ const rowCount = totalRowCount !== null && totalRowCount !== void 0 ? totalRowCount : rows.length;
148
+ if (rows.length === 0) {
149
+ text = "(no rows found)";
150
+ }
151
+ /*
152
+ * Tell the model when it is only seeing a slice. Without this marker a
153
+ * capped result looks complete, and the model reports partial data as if it
154
+ * were the whole picture.
155
+ */
156
+ if (rows.length > MAX_ROWS) {
157
+ text = `${text}\n… [showing the first ${MAX_ROWS} of ${rowCount} rows; narrow the query — a shorter time range or more filters — to see the rest]`;
158
+ }
159
+ return {
160
+ text,
161
+ rowCount,
162
+ redactionCount,
163
+ isTruncated,
164
+ bytes: Buffer.byteLength(text, "utf8"),
165
+ };
166
+ }
167
+ // Serialize free-form text (e.g. a rendered trace tree).
168
+ static serializeText(text, rowCount) {
169
+ const redacted = this.redact(text);
170
+ let output = redacted.text;
171
+ let isTruncated = false;
172
+ if (Buffer.byteLength(output, "utf8") > MAX_PAYLOAD_BYTES) {
173
+ output = `${this.truncateToBytes(output, MAX_PAYLOAD_BYTES)}… [payload truncated]`;
174
+ isTruncated = true;
175
+ }
176
+ if (!output) {
177
+ output = "(no data found)";
178
+ }
179
+ return {
180
+ text: output,
181
+ rowCount,
182
+ redactionCount: redacted.count,
183
+ isTruncated,
184
+ bytes: Buffer.byteLength(output, "utf8"),
185
+ };
186
+ }
187
+ }
188
+ //# sourceMappingURL=Serializer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Serializer.js","sourceRoot":"","sources":["../../../../../../Server/Utils/AI/Toolbox/Serializer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,142 @@
1
+ import BadDataException from "../../../../Types/Exception/BadDataException";
2
+ import ObjectID from "../../../../Types/ObjectID";
3
+ import OneUptimeDate from "../../../../Types/Date";
4
+ /*
5
+ * Defensive argument readers. Tool arguments come from the LLM and are
6
+ * untrusted: everything is type-checked, defaulted and clamped.
7
+ */
8
+ export class ToolArgs {
9
+ static getString(args, key) {
10
+ const value = args[key];
11
+ if (typeof value === "string" && value.trim().length > 0) {
12
+ return value.trim();
13
+ }
14
+ return undefined;
15
+ }
16
+ static getStringArray(args, key) {
17
+ const value = args[key];
18
+ if (Array.isArray(value)) {
19
+ const strings = value.filter((item) => {
20
+ return typeof item === "string" && item.trim().length > 0;
21
+ });
22
+ if (strings.length > 0) {
23
+ return strings;
24
+ }
25
+ }
26
+ return undefined;
27
+ }
28
+ static getNumber(args, key, options) {
29
+ const value = args[key];
30
+ let parsed = options.defaultValue;
31
+ if (typeof value === "number" && Number.isFinite(value)) {
32
+ parsed = value;
33
+ }
34
+ else if (typeof value === "string" && value.trim().length > 0) {
35
+ const fromString = Number(value);
36
+ if (Number.isFinite(fromString)) {
37
+ parsed = fromString;
38
+ }
39
+ }
40
+ return Math.min(options.max, Math.max(options.min, Math.floor(parsed)));
41
+ }
42
+ static getBoolean(args, key) {
43
+ const value = args[key];
44
+ if (typeof value === "boolean") {
45
+ return value;
46
+ }
47
+ if (value === "true") {
48
+ return true;
49
+ }
50
+ if (value === "false") {
51
+ return false;
52
+ }
53
+ return undefined;
54
+ }
55
+ static getObjectID(args, key) {
56
+ const value = this.getString(args, key);
57
+ if (!value) {
58
+ return undefined;
59
+ }
60
+ return new ObjectID(value);
61
+ }
62
+ /*
63
+ * Combines the caller's optional serviceId filter with the user's owned-scope
64
+ * access (from ModelPermission.getAccessibleServiceIdsForAnalyticsModel) into
65
+ * the `serviceIds` filter to hand an aggregation service.
66
+ *
67
+ * `allowed === null` means the user has project-wide access: pass only the
68
+ * caller's own filter (or undefined for no filter). Otherwise the user is
69
+ * label/owned-restricted: intersect with any requested service and NEVER
70
+ * return undefined or an empty array — an empty result is forced to a
71
+ * no-match sentinel, because the aggregation services treat a missing/empty
72
+ * serviceIds as "no filter" (which would leak the whole project).
73
+ */
74
+ static scopeServiceIds(allowed, requested) {
75
+ if (allowed === null) {
76
+ return requested ? [requested] : undefined;
77
+ }
78
+ let effective = allowed;
79
+ if (requested) {
80
+ effective = allowed.filter((id) => {
81
+ return id.toString() === requested.toString();
82
+ });
83
+ }
84
+ if (effective.length === 0) {
85
+ return [ObjectID.getZeroObjectID()];
86
+ }
87
+ return effective;
88
+ }
89
+ /*
90
+ * Time range for a tool call: explicit ISO startTime/endTime arguments,
91
+ * clamped to a maximum window, defaulting to the last hour.
92
+ */
93
+ static getTimeRange(args, options) {
94
+ var _a, _b;
95
+ const defaultHours = (_a = options === null || options === void 0 ? void 0 : options.defaultHours) !== null && _a !== void 0 ? _a : 1;
96
+ const maxDays = (_b = options === null || options === void 0 ? void 0 : options.maxDays) !== null && _b !== void 0 ? _b : 30;
97
+ const endTimeString = this.getString(args, "endTime");
98
+ const startTimeString = this.getString(args, "startTime");
99
+ /*
100
+ * Reject unparseable timestamps instead of silently falling back to the
101
+ * default window — a silent fallback answers a different question than the
102
+ * one asked. Throwing lets the model see the error and retry with a valid
103
+ * ISO 8601 value.
104
+ */
105
+ let endTime = OneUptimeDate.getCurrentDate();
106
+ if (endTimeString) {
107
+ const parsed = new Date(endTimeString);
108
+ if (isNaN(parsed.getTime())) {
109
+ throw new BadDataException(`endTime "${endTimeString}" is not a valid ISO 8601 timestamp (e.g. 2024-01-31T14:00:00Z).`);
110
+ }
111
+ endTime = parsed;
112
+ }
113
+ let startTime = OneUptimeDate.addRemoveHours(endTime, -1 * defaultHours);
114
+ if (startTimeString) {
115
+ const parsed = new Date(startTimeString);
116
+ if (isNaN(parsed.getTime())) {
117
+ throw new BadDataException(`startTime "${startTimeString}" is not a valid ISO 8601 timestamp (e.g. 2024-01-31T13:00:00Z).`);
118
+ }
119
+ startTime = parsed;
120
+ }
121
+ if (startTime.getTime() >= endTime.getTime()) {
122
+ throw new BadDataException("startTime must be before endTime.");
123
+ }
124
+ const maxWindowMs = maxDays * 24 * 60 * 60 * 1000;
125
+ if (endTime.getTime() - startTime.getTime() > maxWindowMs) {
126
+ startTime = new Date(endTime.getTime() - maxWindowMs);
127
+ }
128
+ return { startTime, endTime };
129
+ }
130
+ }
131
+ // Shared JSON-Schema fragments for tool input schemas.
132
+ export const TimeRangeSchemaProperties = {
133
+ startTime: {
134
+ type: "string",
135
+ description: "Start of the time range as an ISO 8601 timestamp. Defaults to one hour before endTime.",
136
+ },
137
+ endTime: {
138
+ type: "string",
139
+ description: "End of the time range as an ISO 8601 timestamp. Defaults to now.",
140
+ },
141
+ };
142
+ //# sourceMappingURL=ToolTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolTypes.js","sourceRoot":"","sources":["../../../../../../Server/Utils/AI/Toolbox/ToolTypes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,309 @@
1
+ import Span from "../../../../Models/AnalyticsModels/Span";
2
+ import DatabaseRequestType from "../../../Types/BaseDatabase/DatabaseRequestType";
3
+ import ModelPermission from "../../../Types/AnalyticsDatabase/ModelPermission";
4
+ import SortOrder from "../../../../Types/BaseDatabase/SortOrder";
5
+ import BadDataException from "../../../../Types/Exception/BadDataException";
6
+ import Permission from "../../../../Types/Permission";
7
+ import { AIChatCitationTargetType, } from "../../../../Types/AI/AIChatTypes";
8
+ import SpanService from "../../../Services/SpanService";
9
+ import TraceAggregationService from "../../../Services/TraceAggregationService";
10
+ import ToolResultSerializer from "./Serializer";
11
+ import WidgetBuilder from "./WidgetBuilder";
12
+ import { TimeRangeSchemaProperties, ToolArgs, } from "./ToolTypes";
13
+ const TRACE_READ_PERMISSIONS = [
14
+ Permission.ProjectOwner,
15
+ Permission.ProjectAdmin,
16
+ Permission.ProjectMember,
17
+ Permission.Viewer,
18
+ Permission.TelemetryAdmin,
19
+ Permission.TelemetryMember,
20
+ Permission.TelemetryViewer,
21
+ Permission.ReadTelemetryServiceTraces,
22
+ ];
23
+ const VALID_METRICS = [
24
+ "count",
25
+ "errorCount",
26
+ "avgDuration",
27
+ "minDuration",
28
+ "maxDuration",
29
+ "p50Duration",
30
+ "p90Duration",
31
+ "p95Duration",
32
+ "p99Duration",
33
+ ];
34
+ const VALID_GROUP_BY = [
35
+ "name",
36
+ "primaryEntityId",
37
+ "kind",
38
+ "statusCode",
39
+ "isRootSpan",
40
+ ];
41
+ /*
42
+ * Upper bound on spans fetched for a single trace waterfall. High enough for
43
+ * almost every real trace; when a trace exceeds it we say so explicitly rather
44
+ * than silently dropping spans (which also orphans their children into fake
45
+ * roots).
46
+ */
47
+ const MAX_TRACE_SPANS = 500;
48
+ export const QueryTracesTool = {
49
+ name: "query_traces",
50
+ description: "Analyze spans/traces: latency profile (count, errorCount, avg and p50/p90/p95/p99 durations in ms) grouped by one dimension such as span name or service. Use this to answer 'why is X slow', 'error rate by operation' or 'slowest endpoints'. Then drill into a single trace with get_trace.",
51
+ inputSchema: {
52
+ type: "object",
53
+ properties: Object.assign(Object.assign({}, TimeRangeSchemaProperties), { groupBy: {
54
+ type: "string",
55
+ enum: ["name", "primaryEntityId", "kind", "statusCode", "isRootSpan"],
56
+ description: "Dimension to group by (default name = span/operation name; primaryEntityId = service).",
57
+ }, metric: {
58
+ type: "string",
59
+ enum: [
60
+ "count",
61
+ "errorCount",
62
+ "avgDuration",
63
+ "minDuration",
64
+ "maxDuration",
65
+ "p50Duration",
66
+ "p90Duration",
67
+ "p95Duration",
68
+ "p99Duration",
69
+ ],
70
+ description: "Metric used to rank the groups (default p95Duration). The full latency profile is always returned per group.",
71
+ }, nameSearchText: {
72
+ type: "string",
73
+ description: "Only spans whose name contains this text.",
74
+ }, serviceId: {
75
+ type: "string",
76
+ description: "Only spans from this telemetry service.",
77
+ }, hasException: {
78
+ type: "boolean",
79
+ description: "Only spans that recorded an exception.",
80
+ }, rootOnly: {
81
+ type: "boolean",
82
+ description: "Only root spans (whole-request latency).",
83
+ }, limit: {
84
+ type: "number",
85
+ description: "Maximum groups to return (default 10, max 25).",
86
+ } }),
87
+ },
88
+ requiredPermissions: TRACE_READ_PERMISSIONS,
89
+ execute: async (args, ctx) => {
90
+ const { startTime, endTime } = ToolArgs.getTimeRange(args, {
91
+ defaultHours: 24,
92
+ maxDays: 30,
93
+ });
94
+ const metric = ToolArgs.getString(args, "metric") || "p95Duration";
95
+ if (!VALID_METRICS.includes(metric)) {
96
+ throw new BadDataException(`Invalid metric: ${metric}`);
97
+ }
98
+ const groupBy = ToolArgs.getString(args, "groupBy") || "name";
99
+ if (!VALID_GROUP_BY.includes(groupBy)) {
100
+ throw new BadDataException(`Invalid groupBy: ${groupBy}`);
101
+ }
102
+ const limit = ToolArgs.getNumber(args, "limit", {
103
+ defaultValue: 10,
104
+ min: 1,
105
+ max: 25,
106
+ });
107
+ const serviceId = ToolArgs.getObjectID(args, "serviceId");
108
+ const windowMinutes = (endTime.getTime() - startTime.getTime()) / (60 * 1000);
109
+ /*
110
+ * getAnalyticsTable builds raw aggregation SQL and skips the model layer's
111
+ * owned-scope filter, so a label-restricted user would otherwise see
112
+ * project-wide trace analytics. Constrain to the services this user may
113
+ * read (spans are owned through Service, same as logs).
114
+ */
115
+ const accessibleServiceIds = await ModelPermission.getAccessibleServiceIdsForAnalyticsModel(Span, ctx.props, DatabaseRequestType.Read);
116
+ const tableRows = await TraceAggregationService.getAnalyticsTable({
117
+ projectId: ctx.projectId,
118
+ startTime: startTime,
119
+ endTime: endTime,
120
+ bucketSizeInMinutes: Math.max(1, Math.round(windowMinutes / 60)),
121
+ chartType: "table",
122
+ metric: metric,
123
+ groupBy: [groupBy],
124
+ limit: limit,
125
+ nameSearchText: ToolArgs.getString(args, "nameSearchText"),
126
+ serviceIds: ToolArgs.scopeServiceIds(accessibleServiceIds, serviceId),
127
+ hasException: ToolArgs.getBoolean(args, "hasException"),
128
+ rootOnly: ToolArgs.getBoolean(args, "rootOnly"),
129
+ });
130
+ const rows = tableRows.map((row) => {
131
+ return {
132
+ [groupBy]: Object.values(row.groupValues).join(" / "),
133
+ count: row.count,
134
+ errorCount: row.errorCount,
135
+ avgMs: Math.round(row.avgDurationMs * 100) / 100,
136
+ p50Ms: Math.round(row.p50DurationMs * 100) / 100,
137
+ p90Ms: Math.round(row.p90DurationMs * 100) / 100,
138
+ p95Ms: Math.round(row.p95DurationMs * 100) / 100,
139
+ p99Ms: Math.round(row.p99DurationMs * 100) / 100,
140
+ };
141
+ });
142
+ const serialized = ToolResultSerializer.serializeRows(rows);
143
+ return {
144
+ dataForLlm: serialized.text,
145
+ rowCount: serialized.rowCount,
146
+ citationLabel: `Trace analytics by ${groupBy} (${metric}), ${startTime.toISOString()} – ${endTime.toISOString()}`,
147
+ citationTarget: {
148
+ type: AIChatCitationTargetType.Traces,
149
+ },
150
+ redactionCount: serialized.redactionCount,
151
+ isTruncated: serialized.isTruncated,
152
+ widget: rows.length > 0
153
+ ? WidgetBuilder.table({
154
+ title: `Trace analytics by ${groupBy}`,
155
+ description: `Ranked by ${metric} · ${startTime.toISOString()} – ${endTime.toISOString()}`,
156
+ columns: [
157
+ { key: groupBy, title: groupBy, type: "text" },
158
+ { key: "count", title: "Count", type: "number" },
159
+ { key: "errorCount", title: "Errors", type: "number" },
160
+ { key: "avgMs", title: "Avg (ms)", type: "number" },
161
+ { key: "p90Ms", title: "p90 (ms)", type: "number" },
162
+ { key: "p95Ms", title: "p95 (ms)", type: "number" },
163
+ { key: "p99Ms", title: "p99 (ms)", type: "number" },
164
+ ],
165
+ rows: rows,
166
+ link: { type: AIChatCitationTargetType.Traces },
167
+ })
168
+ : undefined,
169
+ };
170
+ },
171
+ };
172
+ export const GetTraceTool = {
173
+ name: "get_trace",
174
+ description: "Get the span tree (waterfall) of one trace by traceId: every span with its duration, status and whether it recorded an exception. Use after query_traces or search_logs surfaces an interesting traceId.",
175
+ inputSchema: {
176
+ type: "object",
177
+ properties: {
178
+ traceId: {
179
+ type: "string",
180
+ description: "The trace ID (required).",
181
+ },
182
+ },
183
+ required: ["traceId"],
184
+ },
185
+ requiredPermissions: TRACE_READ_PERMISSIONS,
186
+ execute: async (args, ctx) => {
187
+ var _a, _b;
188
+ const traceId = ToolArgs.getString(args, "traceId");
189
+ if (!traceId) {
190
+ throw new BadDataException("traceId is required.");
191
+ }
192
+ const spans = await SpanService.findBy({
193
+ query: {
194
+ traceId: traceId,
195
+ },
196
+ select: {
197
+ spanId: true,
198
+ parentSpanId: true,
199
+ name: true,
200
+ startTimeUnixNano: true,
201
+ durationUnixNano: true,
202
+ statusCode: true,
203
+ kind: true,
204
+ },
205
+ sort: {
206
+ startTimeUnixNano: SortOrder.Ascending,
207
+ },
208
+ limit: MAX_TRACE_SPANS,
209
+ skip: 0,
210
+ props: ctx.props,
211
+ });
212
+ const isSpanLimitHit = spans.length >= MAX_TRACE_SPANS;
213
+ // Build the tree.
214
+ const nodesBySpanId = new Map();
215
+ const roots = [];
216
+ for (const span of spans) {
217
+ nodesBySpanId.set(((_a = span.spanId) === null || _a === void 0 ? void 0 : _a.toString()) || "", {
218
+ span,
219
+ children: [],
220
+ });
221
+ }
222
+ for (const node of nodesBySpanId.values()) {
223
+ const parentId = ((_b = node.span.parentSpanId) === null || _b === void 0 ? void 0 : _b.toString()) || "";
224
+ const parent = nodesBySpanId.get(parentId);
225
+ if (parent && parent !== node) {
226
+ parent.children.push(node);
227
+ }
228
+ else {
229
+ roots.push(node);
230
+ }
231
+ }
232
+ const lines = [];
233
+ const renderNode = (node, depth) => {
234
+ const durationMs = Math.round((Number(node.span.durationUnixNano) / 1000000) * 100) /
235
+ 100;
236
+ const status = Number(node.span.statusCode) === 2 ? " [ERROR]" : "";
237
+ lines.push(`${" ".repeat(depth)}- ${node.span.name} (${durationMs}ms)${status}`);
238
+ for (const child of node.children) {
239
+ renderNode(child, depth + 1);
240
+ }
241
+ };
242
+ for (const root of roots) {
243
+ renderNode(root, 0);
244
+ }
245
+ if (isSpanLimitHit) {
246
+ lines.push(`… trace truncated at ${MAX_TRACE_SPANS} spans; deeper spans are omitted and some shown here may appear as roots because their parent was cut off.`);
247
+ }
248
+ const serialized = ToolResultSerializer.serializeText(lines.join("\n"), spans.length);
249
+ /*
250
+ * Build the waterfall widget: each span's start is expressed as an offset in
251
+ * ms from the earliest span start (the trace start), so the renderer can lay
252
+ * out proportional bars without any wall-clock math.
253
+ */
254
+ let traceStartNano = undefined;
255
+ let traceEndNano = 0;
256
+ for (const span of spans) {
257
+ const start = Number(span.startTimeUnixNano);
258
+ const end = start + Number(span.durationUnixNano);
259
+ if (traceStartNano === undefined || start < traceStartNano) {
260
+ traceStartNano = start;
261
+ }
262
+ if (end > traceEndNano) {
263
+ traceEndNano = end;
264
+ }
265
+ }
266
+ const nanoToMs = (nano) => {
267
+ return Math.round((nano / 1000000) * 100) / 100;
268
+ };
269
+ const widgetSpans = spans.map((span) => {
270
+ var _a, _b;
271
+ const start = Number(span.startTimeUnixNano);
272
+ return {
273
+ spanId: ((_a = span.spanId) === null || _a === void 0 ? void 0 : _a.toString()) || "",
274
+ parentSpanId: ((_b = span.parentSpanId) === null || _b === void 0 ? void 0 : _b.toString()) || undefined,
275
+ name: span.name || "(unnamed span)",
276
+ startOffsetMs: nanoToMs(start - (traceStartNano !== null && traceStartNano !== void 0 ? traceStartNano : start)),
277
+ durationMs: nanoToMs(Number(span.durationUnixNano)),
278
+ isError: Number(span.statusCode) === 2,
279
+ };
280
+ });
281
+ const totalDurationMs = traceStartNano !== undefined
282
+ ? nanoToMs(traceEndNano - traceStartNano)
283
+ : 0;
284
+ return {
285
+ dataForLlm: serialized.text,
286
+ rowCount: serialized.rowCount,
287
+ citationLabel: `Trace ${traceId} (${spans.length}${isSpanLimitHit ? "+" : ""} spans)`,
288
+ citationTarget: {
289
+ type: AIChatCitationTargetType.TraceView,
290
+ params: { traceId: traceId },
291
+ },
292
+ redactionCount: serialized.redactionCount,
293
+ isTruncated: serialized.isTruncated || isSpanLimitHit,
294
+ widget: widgetSpans.length > 0
295
+ ? WidgetBuilder.traceWaterfall({
296
+ title: `Trace waterfall (${spans.length}${isSpanLimitHit ? "+" : ""} spans)`,
297
+ description: `${totalDurationMs} ms total`,
298
+ spans: widgetSpans,
299
+ totalDurationMs: totalDurationMs,
300
+ link: {
301
+ type: AIChatCitationTargetType.TraceView,
302
+ params: { traceId: traceId },
303
+ },
304
+ })
305
+ : undefined,
306
+ };
307
+ },
308
+ };
309
+ //# sourceMappingURL=TraceTools.js.map