@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.
- 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
|
@@ -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
|