Skip to content

Commit e5a8b65

Browse files
authored
feat: add Get Workunit Metrics tool for performance metrics retrieval (#489)
Signed-off-by: Gordon Smith <[email protected]>
1 parent fd1a8bd commit e5a8b65

File tree

3 files changed

+170
-0
lines changed

3 files changed

+170
-0
lines changed

package.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,32 @@
299299
]
300300
}
301301
},
302+
{
303+
"name": "ecl-extension-getWorkunitMetrics",
304+
"tags": [
305+
"workunit",
306+
"metrics",
307+
"performance",
308+
"ecl-extension"
309+
],
310+
"toolReferenceName": "getWorkunitMetrics",
311+
"displayName": "Get Workunit Metrics",
312+
"modelDescription": "Fetch normalized performance metrics for a specific workunit including execution details, timing information, and resource usage. Requires a WUID (Workunit ID).",
313+
"canBeReferencedInPrompt": true,
314+
"icon": "$(info)",
315+
"inputSchema": {
316+
"type": "object",
317+
"properties": {
318+
"wuid": {
319+
"type": "string",
320+
"description": "The Workunit ID (WUID) to fetch details for"
321+
}
322+
},
323+
"required": [
324+
"wuid"
325+
]
326+
}
327+
},
302328
{
303329
"name": "ecl-extension-syntaxCheck",
304330
"tags": [

src/ecl/lm/tools.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as vscode from "vscode";
22
import { FindWorkunitsTool } from "./tools/findWorkunits";
33
import { GetWorkunitErrorsTool } from "./tools/getWorkunitErrors";
44
import { GetWorkunitECLTool } from "./tools/getWorkunitECL";
5+
import { GetWorkunitMetricsTool } from "./tools/getWorkunitMetrics";
56
import { FindLogicalFilesTool } from "./tools/findLogicalFiles";
67
import { SyntaxCheckTool } from "./tools/syntaxCheck";
78

@@ -13,6 +14,7 @@ export class ECLLMTools {
1314
ctx.subscriptions.push(vscode.lm.registerTool("ecl-extension-findWorkunits", new FindWorkunitsTool()));
1415
ctx.subscriptions.push(vscode.lm.registerTool("ecl-extension-getWorkunitErrors", new GetWorkunitErrorsTool()));
1516
ctx.subscriptions.push(vscode.lm.registerTool("ecl-extension-getWorkunitECL", new GetWorkunitECLTool()));
17+
ctx.subscriptions.push(vscode.lm.registerTool("ecl-extension-getWorkunitMetrics", new GetWorkunitMetricsTool()));
1618

1719
ctx.subscriptions.push(vscode.lm.registerTool("ecl-extension-findLogicalFiles", new FindLogicalFilesTool()));
1820
ctx.subscriptions.push(vscode.lm.registerTool("ecl-extension-syntaxCheck", new SyntaxCheckTool()));
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import * as vscode from "vscode";
2+
import { Workunit } from "@hpcc-js/comms";
3+
import { isPlatformConnected } from "../../../hpccplatform/session";
4+
import { reporter } from "../../../telemetry";
5+
import localize from "../../../util/localize";
6+
import { createServiceOptions, logToolEvent, requireConnectedSession, throwIfCancellationRequested } from "../utils";
7+
8+
export interface IGetWorkunitMetricsParameters {
9+
/**
10+
* Workunit ID (WUID) to fetch metrics for
11+
*/
12+
wuid: string;
13+
}
14+
15+
export class GetWorkunitMetricsTool implements vscode.LanguageModelTool<IGetWorkunitMetricsParameters> {
16+
async invoke(options: vscode.LanguageModelToolInvocationOptions<IGetWorkunitMetricsParameters>, token: vscode.CancellationToken) {
17+
reporter?.sendTelemetryEvent("lmTool.invoke", { tool: "getWorkunitMetrics" });
18+
const params = options.input;
19+
20+
const wuid = typeof params.wuid === "string" ? params.wuid.trim() : "";
21+
if (wuid.length === 0) {
22+
throw new vscode.LanguageModelError(localize("WUID is required"), { cause: "invalid_parameters" });
23+
}
24+
25+
logToolEvent("getWorkunitMetrics", "invoke start", { wuid });
26+
27+
const session = requireConnectedSession();
28+
const opts = await createServiceOptions(session);
29+
30+
try {
31+
// Attach to the workunit and fetch its normalized metrics
32+
const wu = Workunit.attach(opts, wuid);
33+
await wu.refresh();
34+
35+
throwIfCancellationRequested(token);
36+
37+
// Fetch normalized details (metrics)
38+
const { data } = await wu.fetchDetailsNormalized({
39+
ScopeFilter: {
40+
MaxDepth: 999999,
41+
ScopeTypes: []
42+
},
43+
NestedFilter: {
44+
Depth: 0,
45+
ScopeTypes: []
46+
},
47+
PropertiesToReturn: {
48+
AllScopes: true,
49+
AllAttributes: true,
50+
AllProperties: true,
51+
AllNotes: true,
52+
AllStatistics: true,
53+
AllHints: true
54+
},
55+
ScopeOptions: {
56+
IncludeId: true,
57+
IncludeScope: true,
58+
IncludeScopeType: true,
59+
IncludeMatchedScopesInResults: true
60+
},
61+
PropertyOptions: {
62+
IncludeName: true,
63+
IncludeRawValue: true,
64+
IncludeFormatted: true,
65+
IncludeMeasure: true,
66+
IncludeCreator: false,
67+
IncludeCreatorType: false
68+
}
69+
});
70+
71+
throwIfCancellationRequested(token);
72+
73+
const parts: vscode.LanguageModelTextPart[] = [];
74+
75+
// Add workunit summary information
76+
const detailsUrl = session.wuDetailsUrl(wu.Wuid);
77+
parts.push(new vscode.LanguageModelTextPart(localize("Workunit Metrics for {0}:", wuid)));
78+
79+
const wuSummary = {
80+
wuid: wu.Wuid,
81+
owner: wu.Owner,
82+
cluster: wu.Cluster,
83+
state: wu.State,
84+
stateID: wu.StateID,
85+
jobname: wu.Jobname,
86+
totalClusterTime: wu.TotalClusterTime,
87+
};
88+
89+
parts.push(new vscode.LanguageModelTextPart(JSON.stringify(wuSummary, null, 2)));
90+
91+
const summary = localize(
92+
"{0} on {1} is {2}.",
93+
wu.Wuid,
94+
wu.Cluster || localize("unknown cluster"),
95+
wu.State || localize("unknown state")
96+
);
97+
parts.push(new vscode.LanguageModelTextPart(summary));
98+
if (detailsUrl) {
99+
parts.push(new vscode.LanguageModelTextPart(`${localize("ECL Watch URL:")} ${detailsUrl}`));
100+
}
101+
102+
// Add normalized metrics data
103+
if (data && data.length > 0) {
104+
parts.push(new vscode.LanguageModelTextPart(localize("Metrics ({0} scopes):", data.length.toString())));
105+
parts.push(new vscode.LanguageModelTextPart(JSON.stringify({
106+
scopeCount: data.length,
107+
scopeJson: data
108+
}, null, 2)));
109+
}
110+
111+
logToolEvent("getWorkunitMetrics", "invoke success", {
112+
wuid: wu.Wuid,
113+
state: wu.State,
114+
scopeCount: data?.length || 0,
115+
});
116+
117+
return new vscode.LanguageModelToolResult(parts);
118+
} catch (error) {
119+
const errorMessage = error instanceof Error ? error.message : String(error);
120+
logToolEvent("getWorkunitMetrics", "invoke failed", { wuid, error: errorMessage });
121+
throw new vscode.LanguageModelError(
122+
localize("Failed to fetch workunit metrics: {0}", errorMessage),
123+
{ cause: error }
124+
);
125+
}
126+
}
127+
128+
async prepareInvocation(options: vscode.LanguageModelToolInvocationPrepareOptions<IGetWorkunitMetricsParameters>, _token: vscode.CancellationToken) {
129+
const connected = isPlatformConnected();
130+
const wuid = typeof options.input.wuid === "string" ? options.input.wuid.trim() : "";
131+
132+
return {
133+
invocationMessage: connected
134+
? localize("Fetching metrics for workunit {0}", wuid || localize("(unspecified)"))
135+
: localize("Cannot fetch: HPCC Platform not connected"),
136+
confirmationMessages: connected ? undefined : {
137+
title: localize("HPCC Platform not connected"),
138+
message: new vscode.MarkdownString(localize("This tool requires an active HPCC connection.")),
139+
}
140+
};
141+
}
142+
}

0 commit comments

Comments
 (0)