@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,231 @@
|
|
|
1
|
+
import { JSONObject } from "../JSON";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Shared types for the AI observability chat feature. These shapes are
|
|
5
|
+
* persisted as JSON columns on AIConversationMessage / AIRun / AIRunEvent and
|
|
6
|
+
* rendered by the dashboard, so they must stay JSON-serializable.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
* Where a citation chip should deep-link to in the dashboard. The dashboard
|
|
11
|
+
* maps each target type onto its own RouteMap entry.
|
|
12
|
+
*/
|
|
13
|
+
export enum AIChatCitationTargetType {
|
|
14
|
+
Logs = "Logs",
|
|
15
|
+
Traces = "Traces",
|
|
16
|
+
TraceView = "TraceView",
|
|
17
|
+
Metrics = "Metrics",
|
|
18
|
+
Exceptions = "Exceptions",
|
|
19
|
+
Incidents = "Incidents",
|
|
20
|
+
IncidentView = "IncidentView",
|
|
21
|
+
Alerts = "Alerts",
|
|
22
|
+
AlertView = "AlertView",
|
|
23
|
+
Monitors = "Monitors",
|
|
24
|
+
MonitorView = "MonitorView",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface AIChatCitationTarget {
|
|
28
|
+
type: AIChatCitationTargetType;
|
|
29
|
+
// Route params keyed by param name (e.g. { traceId: "abc" }).
|
|
30
|
+
params?: { [key: string]: string };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/*
|
|
34
|
+
* Citations are minted server-side from validated tool arguments — never by
|
|
35
|
+
* the model. rowCount 0 is meaningful: it is proof of absence ("checked,
|
|
36
|
+
* found nothing").
|
|
37
|
+
*/
|
|
38
|
+
export interface AIChatCitation {
|
|
39
|
+
id: string; // "C1", "C2" — sequential per message
|
|
40
|
+
toolName: string;
|
|
41
|
+
label: string;
|
|
42
|
+
queryArguments: JSONObject; // validated tool args as executed — replayable
|
|
43
|
+
rowCount: number;
|
|
44
|
+
target?: AIChatCitationTarget | undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/*
|
|
48
|
+
* ---------------------------------------------------------------------------
|
|
49
|
+
* Inline widgets
|
|
50
|
+
* ---------------------------------------------------------------------------
|
|
51
|
+
* A tool can attach a widget to its result. The widget is persisted on the
|
|
52
|
+
* assistant message (AIConversationMessage.widgets) and rendered inline in the
|
|
53
|
+
* chat by the dashboard. Widget `data` is type-specific and MUST stay
|
|
54
|
+
* JSON-serializable. Unlike the text sent to the LLM, widget data is built from
|
|
55
|
+
* the RAW rows (not the redacted/truncated LLM payload) because it is rendered
|
|
56
|
+
* back to the same user who already has RBAC access to it.
|
|
57
|
+
*/
|
|
58
|
+
export enum AIChatWidgetType {
|
|
59
|
+
// A time-series line chart (e.g. a metric aggregated over time).
|
|
60
|
+
TimeSeriesChart = "TimeSeriesChart",
|
|
61
|
+
// A bar chart, optionally stacked (e.g. log volume by severity over time).
|
|
62
|
+
BarChart = "BarChart",
|
|
63
|
+
// A generic sortable table.
|
|
64
|
+
Table = "Table",
|
|
65
|
+
// A trace waterfall (span tree with durations).
|
|
66
|
+
TraceWaterfall = "TraceWaterfall",
|
|
67
|
+
// A list of incident cards.
|
|
68
|
+
IncidentList = "IncidentList",
|
|
69
|
+
// A list of alert cards.
|
|
70
|
+
AlertList = "AlertList",
|
|
71
|
+
// A list of exception cards.
|
|
72
|
+
ExceptionList = "ExceptionList",
|
|
73
|
+
// KPI stat tiles.
|
|
74
|
+
StatCards = "StatCards",
|
|
75
|
+
// A single created/updated resource (e.g. the incident the agent just made).
|
|
76
|
+
ResourceCard = "ResourceCard",
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/*
|
|
80
|
+
* One point in a chart series. x is an ISO timestamp (time charts) or a
|
|
81
|
+
* category label (categorical charts); y is the numeric value.
|
|
82
|
+
*/
|
|
83
|
+
export interface AIChatWidgetPoint {
|
|
84
|
+
x: string;
|
|
85
|
+
y: number | null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface AIChatWidgetSeries {
|
|
89
|
+
name: string;
|
|
90
|
+
points: Array<AIChatWidgetPoint>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface AIChatWidgetColumn {
|
|
94
|
+
key: string;
|
|
95
|
+
title: string;
|
|
96
|
+
// Rough hint for alignment/formatting in the renderer.
|
|
97
|
+
type?: "text" | "number" | "date" | undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface AIChatWidgetStat {
|
|
101
|
+
label: string;
|
|
102
|
+
value: string | number;
|
|
103
|
+
unit?: string | undefined;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface AIChatWidgetSpan {
|
|
107
|
+
spanId: string;
|
|
108
|
+
parentSpanId?: string | undefined;
|
|
109
|
+
name: string;
|
|
110
|
+
// Offset of the span start from the trace start, in milliseconds.
|
|
111
|
+
startOffsetMs: number;
|
|
112
|
+
durationMs: number;
|
|
113
|
+
isError: boolean;
|
|
114
|
+
serviceName?: string | undefined;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/*
|
|
118
|
+
* Type-specific widget payload. Only the fields relevant to `type` are set:
|
|
119
|
+
* - TimeSeriesChart / BarChart: `series` (+ `stacked`, `unit`, `xIsTime`)
|
|
120
|
+
* - Table: `columns` + `rows`
|
|
121
|
+
* - TraceWaterfall: `spans` + `totalDurationMs`
|
|
122
|
+
* - IncidentList / AlertList / ExceptionList: `items`
|
|
123
|
+
* - StatCards: `stats`
|
|
124
|
+
* - ResourceCard: `resourceType`, `heading`, `subheading`, `fields`, `link`
|
|
125
|
+
*/
|
|
126
|
+
export interface AIChatWidgetData {
|
|
127
|
+
series?: Array<AIChatWidgetSeries> | undefined;
|
|
128
|
+
stacked?: boolean | undefined;
|
|
129
|
+
xIsTime?: boolean | undefined;
|
|
130
|
+
unit?: string | undefined;
|
|
131
|
+
valueLabel?: string | undefined;
|
|
132
|
+
columns?: Array<AIChatWidgetColumn> | undefined;
|
|
133
|
+
rows?: Array<JSONObject> | undefined;
|
|
134
|
+
spans?: Array<AIChatWidgetSpan> | undefined;
|
|
135
|
+
totalDurationMs?: number | undefined;
|
|
136
|
+
items?: Array<JSONObject> | undefined;
|
|
137
|
+
stats?: Array<AIChatWidgetStat> | undefined;
|
|
138
|
+
resourceType?: string | undefined;
|
|
139
|
+
heading?: string | undefined;
|
|
140
|
+
subheading?: string | undefined;
|
|
141
|
+
fields?: Array<{ label: string; value: string }> | undefined;
|
|
142
|
+
link?: AIChatCitationTarget | undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface AIChatWidget {
|
|
146
|
+
id: string; // "W1", "W2" — sequential per message
|
|
147
|
+
citationId?: string | undefined; // correlates to the citation of the same tool call
|
|
148
|
+
type: AIChatWidgetType;
|
|
149
|
+
title: string;
|
|
150
|
+
description?: string | undefined;
|
|
151
|
+
data: AIChatWidgetData;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/*
|
|
155
|
+
* ---------------------------------------------------------------------------
|
|
156
|
+
* Tool actions (mutations + approvals)
|
|
157
|
+
* ---------------------------------------------------------------------------
|
|
158
|
+
* When the agent wants to run a mutating tool it records an AIChatToolAction on
|
|
159
|
+
* the assistant message. In "Ask for approval" mode the action starts Pending
|
|
160
|
+
* and the run pauses until the user approves/denies it; the resumed run then
|
|
161
|
+
* executes approved actions and marks them Executed/Failed.
|
|
162
|
+
*/
|
|
163
|
+
export enum AIChatToolActionStatus {
|
|
164
|
+
Pending = "Pending", // awaiting the user's decision
|
|
165
|
+
Approved = "Approved", // user approved; not yet executed
|
|
166
|
+
Denied = "Denied", // user declined
|
|
167
|
+
Executed = "Executed", // ran successfully
|
|
168
|
+
Failed = "Failed", // ran but failed
|
|
169
|
+
Skipped = "Skipped", // not run (e.g. budget exhausted)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface AIChatToolAction {
|
|
173
|
+
id: string; // equals the LLM tool call id
|
|
174
|
+
toolName: string;
|
|
175
|
+
title: string; // human label, e.g. "Create incident: Checkout is down"
|
|
176
|
+
description?: string | undefined;
|
|
177
|
+
arguments: JSONObject;
|
|
178
|
+
isMutation: boolean;
|
|
179
|
+
requiresApproval: boolean;
|
|
180
|
+
status: AIChatToolActionStatus;
|
|
181
|
+
resultSummary?: string | undefined;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface AIRunEventResultSummary {
|
|
185
|
+
rowCount?: number | undefined;
|
|
186
|
+
durationInMs?: number | undefined;
|
|
187
|
+
isTruncated?: boolean | undefined;
|
|
188
|
+
bytesSentToLlm?: number | undefined;
|
|
189
|
+
errorMessage?: string | undefined;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// What was sent to which LLM during a run — the per-run egress manifest.
|
|
193
|
+
export interface AIRunEgressManifestToolEntry {
|
|
194
|
+
toolName: string;
|
|
195
|
+
rowCount: number;
|
|
196
|
+
bytesSentToLlm: number;
|
|
197
|
+
redactionCount: number;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/*
|
|
201
|
+
* The serialized in-flight state of a turn that paused to wait for tool
|
|
202
|
+
* approval. Persisted on AIRun.pausedState so ChatAgentRunner.resumeTurn can
|
|
203
|
+
* rehydrate the exact conversation and continue the loop. `messages` and
|
|
204
|
+
* `pendingToolCalls` are JSON projections of the runner's internal
|
|
205
|
+
* LLMMessage / LLMToolCall types (kept as JSONObject here so this shared type
|
|
206
|
+
* does not depend on server code).
|
|
207
|
+
*/
|
|
208
|
+
export interface AIRunPausedState {
|
|
209
|
+
messages: Array<JSONObject>;
|
|
210
|
+
pendingToolCalls: Array<JSONObject>;
|
|
211
|
+
llmCallCount: number;
|
|
212
|
+
toolCallCount: number;
|
|
213
|
+
totalTokens: number;
|
|
214
|
+
totalCostInUSDCents: number;
|
|
215
|
+
eventSequence: number;
|
|
216
|
+
citations: Array<AIChatCitation>;
|
|
217
|
+
widgets: Array<AIChatWidget>;
|
|
218
|
+
toolActions: Array<AIChatToolAction>;
|
|
219
|
+
egressToolEntries: Array<AIRunEgressManifestToolEntry>;
|
|
220
|
+
startedAtMs: number;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export interface AIRunEgressManifest {
|
|
224
|
+
llmProviderName?: string | undefined;
|
|
225
|
+
llmType?: string | undefined;
|
|
226
|
+
modelName?: string | undefined;
|
|
227
|
+
isGlobalProvider?: boolean | undefined;
|
|
228
|
+
llmCallCount: number;
|
|
229
|
+
totalTokens: number;
|
|
230
|
+
toolDataSentToLlm: Array<AIRunEgressManifestToolEntry>;
|
|
231
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
enum AIRunEventType {
|
|
2
|
+
RunStarted = "RunStarted",
|
|
3
|
+
LlmCallStarted = "LlmCallStarted",
|
|
4
|
+
LlmCallCompleted = "LlmCallCompleted",
|
|
5
|
+
ToolCallStarted = "ToolCallStarted",
|
|
6
|
+
ToolCallCompleted = "ToolCallCompleted",
|
|
7
|
+
ToolCallFailed = "ToolCallFailed",
|
|
8
|
+
// The run paused to ask the user to approve one or more mutating actions.
|
|
9
|
+
ApprovalRequested = "ApprovalRequested",
|
|
10
|
+
// A mutating action the user approved was executed (or denied and skipped).
|
|
11
|
+
ActionExecuted = "ActionExecuted",
|
|
12
|
+
ActionDenied = "ActionDenied",
|
|
13
|
+
RunCompleted = "RunCompleted",
|
|
14
|
+
RunFailed = "RunFailed",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default AIRunEventType;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
enum AIRunStatus {
|
|
2
|
+
Running = "Running",
|
|
3
|
+
/*
|
|
4
|
+
* The run paused mid-turn to wait for the user to approve pending tool
|
|
5
|
+
* actions (see AIChatPermissionMode.AskForApproval). It is not terminal — it
|
|
6
|
+
* resumes when the user responds — but it is also not actively consuming
|
|
7
|
+
* compute, so the stale-run sweeper (which only touches Running runs) leaves
|
|
8
|
+
* it alone.
|
|
9
|
+
*/
|
|
10
|
+
WaitingForApproval = "WaitingForApproval",
|
|
11
|
+
Completed = "Completed",
|
|
12
|
+
Error = "Error",
|
|
13
|
+
Cancelled = "Cancelled",
|
|
14
|
+
Stale = "Stale",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default AIRunStatus;
|
|
18
|
+
|
|
19
|
+
export class AIRunStatusHelper {
|
|
20
|
+
public static isTerminalStatus(status: AIRunStatus): boolean {
|
|
21
|
+
return (
|
|
22
|
+
status === AIRunStatus.Completed ||
|
|
23
|
+
status === AIRunStatus.Error ||
|
|
24
|
+
status === AIRunStatus.Cancelled ||
|
|
25
|
+
status === AIRunStatus.Stale
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
package/Types/LLM/LlmType.ts
CHANGED
|
@@ -5,6 +5,12 @@ enum LlmType {
|
|
|
5
5
|
Groq = "Groq",
|
|
6
6
|
Mistral = "Mistral",
|
|
7
7
|
Ollama = "Ollama",
|
|
8
|
+
/*
|
|
9
|
+
* Generic OpenAI-compatible servers (vLLM, LocalAI, LM Studio, text-gen-webui,
|
|
10
|
+
* etc.) that speak the OpenAI /chat/completions wire format but are typically
|
|
11
|
+
* self-hosted at a custom base URL and often require no API key.
|
|
12
|
+
*/
|
|
13
|
+
OpenAICompatible = "OpenAICompatible",
|
|
8
14
|
}
|
|
9
15
|
|
|
10
16
|
export default LlmType;
|
|
@@ -37,21 +37,28 @@ const HeaderIconDropdownButton: FunctionComponent<ComponentProps> = (
|
|
|
37
37
|
setIsComponentVisible(Boolean(props.showDropdown));
|
|
38
38
|
}, [props.showDropdown]);
|
|
39
39
|
|
|
40
|
+
const hasLabel: boolean = Boolean(props.title);
|
|
41
|
+
const hasDropdown: boolean = Boolean(props.children);
|
|
42
|
+
|
|
43
|
+
const sizeClassName: string = hasLabel
|
|
44
|
+
? "h-9 gap-1.5 pl-2.5 pr-3"
|
|
45
|
+
: "h-9 w-9 justify-center";
|
|
46
|
+
|
|
40
47
|
return (
|
|
41
|
-
<div className="relative ml-
|
|
48
|
+
<div className="relative ml-2 flex-shrink-0">
|
|
42
49
|
<div>
|
|
43
50
|
<button
|
|
44
51
|
type="button"
|
|
45
|
-
className=
|
|
46
|
-
id="user-menu-button"
|
|
47
|
-
aria-expanded=
|
|
48
|
-
aria-haspopup="true"
|
|
52
|
+
className={`flex items-center rounded-lg border border-gray-200 bg-gray-50 hover:bg-gray-100 hover:border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-1 transition-all duration-150 ${sizeClassName}`}
|
|
53
|
+
id={hasDropdown ? "user-menu-button" : undefined}
|
|
54
|
+
aria-expanded={hasDropdown ? isDropdownVisible : undefined}
|
|
55
|
+
aria-haspopup={hasDropdown ? "true" : undefined}
|
|
49
56
|
onClick={() => {
|
|
50
57
|
props.onClick?.();
|
|
51
58
|
setIsComponentVisible(!isDropdownVisible);
|
|
52
59
|
}}
|
|
53
60
|
>
|
|
54
|
-
<span className="sr-only">{props.name}</span>
|
|
61
|
+
{!hasLabel && <span className="sr-only">{props.name}</span>}
|
|
55
62
|
{props.iconImageUrl && (
|
|
56
63
|
<Image
|
|
57
64
|
className="h-7 w-7 rounded-md object-cover"
|
|
@@ -63,14 +70,17 @@ const HeaderIconDropdownButton: FunctionComponent<ComponentProps> = (
|
|
|
63
70
|
/>
|
|
64
71
|
)}
|
|
65
72
|
{props.icon && (
|
|
66
|
-
<Icon
|
|
73
|
+
<Icon
|
|
74
|
+
className={`${hasLabel ? "h-4 w-4" : "h-5 w-5"} text-gray-500`}
|
|
75
|
+
icon={props.icon}
|
|
76
|
+
/>
|
|
77
|
+
)}
|
|
78
|
+
{hasLabel && (
|
|
79
|
+
<span className="text-sm font-medium text-gray-700 whitespace-nowrap">
|
|
80
|
+
{props.title}
|
|
81
|
+
</span>
|
|
67
82
|
)}
|
|
68
83
|
</button>
|
|
69
|
-
{props.title && (
|
|
70
|
-
<span className="ml-2 text-sm font-medium text-gray-700">
|
|
71
|
-
{props.title}
|
|
72
|
-
</span>
|
|
73
|
-
)}
|
|
74
84
|
{props.badge && props.badge > 0 && (
|
|
75
85
|
<span className="absolute -top-1 -right-1 flex h-5 w-5 items-center justify-center rounded-full bg-red-500 text-xs font-semibold text-white ring-2 ring-white">
|
|
76
86
|
{props.badge}
|
|
@@ -144,6 +144,14 @@ const MermaidDiagram: FunctionComponent<{ chart: string }> = ({
|
|
|
144
144
|
|
|
145
145
|
export interface ComponentProps {
|
|
146
146
|
text: string;
|
|
147
|
+
/*
|
|
148
|
+
* Hardened rendering for untrusted / LLM-generated content. When true, links
|
|
149
|
+
* render as non-clickable text and images are never fetched — both are
|
|
150
|
+
* neutralized on react-markdown's PARSED tree, so (unlike pre-processing the
|
|
151
|
+
* raw markdown with regexes) no CommonMark syntax trick can smuggle a
|
|
152
|
+
* clickable exfil link or a zero-click tracking pixel through.
|
|
153
|
+
*/
|
|
154
|
+
safeMode?: boolean;
|
|
147
155
|
}
|
|
148
156
|
|
|
149
157
|
// Language display names
|
|
@@ -348,6 +356,9 @@ const extractTextFromChildren: (children: unknown) => string = (
|
|
|
348
356
|
const MarkdownViewer: FunctionComponent<ComponentProps> = (
|
|
349
357
|
props: ComponentProps,
|
|
350
358
|
): ReactElement => {
|
|
359
|
+
// Captured here because the component overrides below shadow `props`.
|
|
360
|
+
const safeMode: boolean = props.safeMode === true;
|
|
361
|
+
|
|
351
362
|
return (
|
|
352
363
|
<div className="max-w-none">
|
|
353
364
|
<ReactMarkdown
|
|
@@ -409,19 +420,40 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
|
|
|
409
420
|
/>
|
|
410
421
|
);
|
|
411
422
|
},
|
|
412
|
-
a: ({ ...props }: any) => {
|
|
423
|
+
a: ({ children, ...props }: any) => {
|
|
424
|
+
if (safeMode) {
|
|
425
|
+
// Keep the link text, drop the href: no navigation, no exfil.
|
|
426
|
+
return (
|
|
427
|
+
<span className="underline decoration-dotted text-gray-700 font-medium">
|
|
428
|
+
{children}
|
|
429
|
+
</span>
|
|
430
|
+
);
|
|
431
|
+
}
|
|
413
432
|
return (
|
|
414
433
|
<a
|
|
415
434
|
className="underline text-blue-600 hover:text-blue-800 font-medium transition-colors"
|
|
416
435
|
{...props}
|
|
417
|
-
|
|
436
|
+
>
|
|
437
|
+
{children}
|
|
438
|
+
</a>
|
|
418
439
|
);
|
|
419
440
|
},
|
|
420
|
-
img: ({ ...props }: any) => {
|
|
441
|
+
img: ({ alt, ...props }: any) => {
|
|
442
|
+
if (safeMode) {
|
|
443
|
+
/*
|
|
444
|
+
* Never emit an <img> for untrusted content — the browser would
|
|
445
|
+
* fetch its src on render (a zero-click exfil channel). Show the
|
|
446
|
+
* alt text (already plain, React-escaped) instead.
|
|
447
|
+
*/
|
|
448
|
+
return alt ? (
|
|
449
|
+
<span className="text-gray-500 italic">{`[image: ${alt}]`}</span>
|
|
450
|
+
) : null;
|
|
451
|
+
}
|
|
421
452
|
return (
|
|
422
453
|
<img
|
|
423
454
|
className="max-w-full h-auto rounded-md border border-gray-200 my-3"
|
|
424
455
|
loading="lazy"
|
|
456
|
+
alt={alt}
|
|
425
457
|
{...props}
|
|
426
458
|
/>
|
|
427
459
|
);
|
|
@@ -10,6 +10,12 @@ import React, { FunctionComponent, ReactElement, useEffect } from "react";
|
|
|
10
10
|
|
|
11
11
|
export interface ComponentProps {
|
|
12
12
|
title?: string | undefined;
|
|
13
|
+
/*
|
|
14
|
+
* Optional one-line subtitle rendered under the title. Use it to say what the
|
|
15
|
+
* page is for when the title alone is not self-explanatory. Kept optional so
|
|
16
|
+
* existing pages that carry their description in a content card are unchanged.
|
|
17
|
+
*/
|
|
18
|
+
description?: string | undefined;
|
|
13
19
|
breadcrumbLinks?: Array<Link> | undefined;
|
|
14
20
|
children: Array<ReactElement> | ReactElement;
|
|
15
21
|
sideMenu?: undefined | ReactElement;
|
|
@@ -25,6 +31,9 @@ const Page: FunctionComponent<ComponentProps> = (
|
|
|
25
31
|
): ReactElement => {
|
|
26
32
|
const { translateString } = useTranslateValue();
|
|
27
33
|
const translatedTitle: string | undefined = translateString(props.title);
|
|
34
|
+
const translatedDescription: string | undefined = translateString(
|
|
35
|
+
props.description,
|
|
36
|
+
);
|
|
28
37
|
|
|
29
38
|
useEffect(() => {
|
|
30
39
|
if (props.breadcrumbLinks && props.breadcrumbLinks.length > 0) {
|
|
@@ -92,6 +101,11 @@ const Page: FunctionComponent<ComponentProps> = (
|
|
|
92
101
|
<h1 className="text-xl font-semibold leading-7 text-gray-900 sm:text-xl sm:tracking-tight sm:truncate">
|
|
93
102
|
{translatedTitle}
|
|
94
103
|
</h1>
|
|
104
|
+
{translatedDescription && (
|
|
105
|
+
<p className="max-w-3xl text-sm leading-6 text-gray-500">
|
|
106
|
+
{translatedDescription}
|
|
107
|
+
</p>
|
|
108
|
+
)}
|
|
95
109
|
</div>
|
|
96
110
|
{props.headerRight && (
|
|
97
111
|
<div className="flex flex-wrap items-center sm:justify-end gap-3">
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { DropdownOption } from "../Components/Dropdown/Dropdown";
|
|
2
|
+
import LlmType from "../../Types/LLM/LlmType";
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Human-friendly display labels for each LLM provider. The dropdown `value`
|
|
6
|
+
* remains the raw LlmType enum value (which is persisted in the database), so
|
|
7
|
+
* only the label shown to the user changes.
|
|
8
|
+
*/
|
|
9
|
+
const LlmTypeDropdownOptions: Array<DropdownOption> = [
|
|
10
|
+
{
|
|
11
|
+
label: "OpenAI",
|
|
12
|
+
value: LlmType.OpenAI,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
label: "Azure OpenAI",
|
|
16
|
+
value: LlmType.AzureOpenAI,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
label: "Anthropic",
|
|
20
|
+
value: LlmType.Anthropic,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
label: "Groq",
|
|
24
|
+
value: LlmType.Groq,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
label: "Mistral",
|
|
28
|
+
value: LlmType.Mistral,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
label: "Ollama",
|
|
32
|
+
value: LlmType.Ollama,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
label: "OpenAI-Compatible (vLLM, LocalAI, etc.)",
|
|
36
|
+
value: LlmType.OpenAICompatible,
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
export default LlmTypeDropdownOptions;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import URL from "../../Types/API/URL";
|
|
2
|
+
import Dictionary from "../../Types/Dictionary";
|
|
3
|
+
import HTTPErrorResponse from "../../Types/API/HTTPErrorResponse";
|
|
4
|
+
import HTTPResponse from "../../Types/API/HTTPResponse";
|
|
5
|
+
import { JSONObject } from "../../Types/JSON";
|
|
6
|
+
import { APP_API_URL } from "../Config";
|
|
7
|
+
import API from "./API/API";
|
|
8
|
+
|
|
9
|
+
export interface LLMProviderTestResult {
|
|
10
|
+
success: boolean;
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default class TestLLMProvider {
|
|
15
|
+
/*
|
|
16
|
+
* Sends a small test prompt to an LLM provider (by id) via the
|
|
17
|
+
* /llm-provider/test endpoint and returns a friendly success/failure result.
|
|
18
|
+
* Callers pass the appropriate common headers (project tenant header for
|
|
19
|
+
* project providers, or none for admin/global providers).
|
|
20
|
+
*/
|
|
21
|
+
public static async test(data: {
|
|
22
|
+
llmProviderId: string;
|
|
23
|
+
headers?: Dictionary<string> | undefined;
|
|
24
|
+
}): Promise<LLMProviderTestResult> {
|
|
25
|
+
try {
|
|
26
|
+
const response: HTTPResponse<JSONObject> | HTTPErrorResponse =
|
|
27
|
+
await API.post({
|
|
28
|
+
url: URL.fromString(APP_API_URL.toString()).addRoute(
|
|
29
|
+
"/llm-provider/test",
|
|
30
|
+
),
|
|
31
|
+
data: {
|
|
32
|
+
llmProviderId: data.llmProviderId,
|
|
33
|
+
},
|
|
34
|
+
...(data.headers ? { headers: data.headers } : {}),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (response.isFailure()) {
|
|
38
|
+
return {
|
|
39
|
+
success: false,
|
|
40
|
+
message: API.getFriendlyMessage(response),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const responseData: JSONObject = response.data as JSONObject;
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
success: true,
|
|
48
|
+
message:
|
|
49
|
+
(responseData["message"] as string) ||
|
|
50
|
+
"Connection successful. The LLM provider responded to a test prompt.",
|
|
51
|
+
};
|
|
52
|
+
} catch (err) {
|
|
53
|
+
return {
|
|
54
|
+
success: false,
|
|
55
|
+
message: API.getFriendlyMessage(err),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|