Skip to content

Commit cf3c429

Browse files
committed
feat: add AI tool to fetch ECL Archive
Signed-off-by: Gordon Smith<GordonJSmith@gmail.com>
1 parent cbacf13 commit cf3c429

File tree

4 files changed

+120
-6
lines changed

4 files changed

+120
-6
lines changed

package.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,34 @@
271271
]
272272
}
273273
},
274+
{
275+
"name": "ecl-extension-getWorkunitECL",
276+
"tags": [
277+
"workunit",
278+
"ecl",
279+
"archive",
280+
"source",
281+
"code",
282+
"ecl-extension"
283+
],
284+
"toolReferenceName": "getWorkunitECL",
285+
"displayName": "Get Workunit ECL / ECL Archive",
286+
"modelDescription": "Retrieve the ECL archive (XML) for a specific workunit. Returns the original ECL code that was submitted to create the workunit. Requires a WUID (Workunit ID).",
287+
"canBeReferencedInPrompt": true,
288+
"icon": "$(code)",
289+
"inputSchema": {
290+
"type": "object",
291+
"properties": {
292+
"wuid": {
293+
"type": "string",
294+
"description": "The Workunit ID (WUID) to fetch ECL source for"
295+
}
296+
},
297+
"required": [
298+
"wuid"
299+
]
300+
}
301+
},
274302
{
275303
"name": "ecl-extension-syntaxCheck",
276304
"tags": [

src/ecl/lm/tools.ts

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

@@ -11,6 +12,7 @@ export class ECLLMTools {
1112
protected constructor(ctx: vscode.ExtensionContext) {
1213
ctx.subscriptions.push(vscode.lm.registerTool("ecl-extension-findWorkunits", new FindWorkunitsTool()));
1314
ctx.subscriptions.push(vscode.lm.registerTool("ecl-extension-getWorkunitErrors", new GetWorkunitErrorsTool()));
15+
ctx.subscriptions.push(vscode.lm.registerTool("ecl-extension-getWorkunitECL", new GetWorkunitECLTool()));
1416

1517
ctx.subscriptions.push(vscode.lm.registerTool("ecl-extension-findLogicalFiles", new FindLogicalFilesTool()));
1618
ctx.subscriptions.push(vscode.lm.registerTool("ecl-extension-syntaxCheck", new SyntaxCheckTool()));

src/ecl/lm/tools/getWorkunitECL.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
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/index";
7+
8+
export interface IGetWorkunitECLParameters {
9+
wuid: string;
10+
}
11+
12+
export class GetWorkunitECLTool implements vscode.LanguageModelTool<IGetWorkunitECLParameters> {
13+
async invoke(options: vscode.LanguageModelToolInvocationOptions<IGetWorkunitECLParameters>, token: vscode.CancellationToken) {
14+
reporter?.sendTelemetryEvent("lmTool.invoke", { tool: "getWorkunitECL" });
15+
const params = options.input;
16+
17+
const wuid = typeof params.wuid === "string" ? params.wuid.trim() : "";
18+
if (wuid.length === 0) {
19+
throw new vscode.LanguageModelError(localize("WUID is required"), { cause: "invalid_parameters" });
20+
}
21+
22+
logToolEvent("getWorkunitECL", "invoke start", { wuid });
23+
24+
const session = requireConnectedSession();
25+
const opts = await createServiceOptions(session);
26+
27+
try {
28+
const wu = Workunit.attach(opts, wuid);
29+
await wu.refresh();
30+
31+
throwIfCancellationRequested(token);
32+
33+
const parts: vscode.LanguageModelTextPart[] = [];
34+
35+
const detailsUrl = session.wuDetailsUrl(wu.Wuid);
36+
parts.push(new vscode.LanguageModelTextPart(localize("ECL Source for Workunit {0}:", wuid)));
37+
38+
const summary = localize(
39+
"Workunit {0} on {1} ({2})",
40+
wu.Wuid,
41+
wu.Cluster || localize("unknown cluster"),
42+
wu.State || localize("unknown state")
43+
);
44+
parts.push(new vscode.LanguageModelTextPart(summary));
45+
if (detailsUrl) {
46+
parts.push(new vscode.LanguageModelTextPart(`${localize("ECL Watch URL:")} ${detailsUrl}`));
47+
}
48+
49+
throwIfCancellationRequested(token);
50+
const eclArchive = await wu.fetchArchive().catch(() => "");
51+
52+
if (eclArchive) {
53+
parts.push(new vscode.LanguageModelTextPart(localize("\nECL Archive (XML format):")));
54+
parts.push(new vscode.LanguageModelTextPart("```xml\n" + eclArchive + "\n```"));
55+
} else {
56+
parts.push(new vscode.LanguageModelTextPart(localize("No ECL archive available for this workunit.")));
57+
}
58+
59+
logToolEvent("getWorkunitECL", "invoke success", {
60+
wuid: wu.Wuid,
61+
state: wu.State,
62+
hasECL: !!eclArchive,
63+
});
64+
65+
return new vscode.LanguageModelToolResult(parts);
66+
} catch (error) {
67+
const errorMessage = error instanceof Error ? error.message : String(error);
68+
logToolEvent("getWorkunitECL", "invoke failed", { wuid, error: errorMessage });
69+
throw new vscode.LanguageModelError(
70+
localize("Failed to fetch workunit ECL: {0}", errorMessage),
71+
{ cause: error }
72+
);
73+
}
74+
}
75+
76+
async prepareInvocation(options: vscode.LanguageModelToolInvocationPrepareOptions<IGetWorkunitECLParameters>, _token: vscode.CancellationToken) {
77+
const connected = isPlatformConnected();
78+
const wuid = typeof options.input.wuid === "string" ? options.input.wuid.trim() : "";
79+
80+
return {
81+
invocationMessage: connected
82+
? localize("Fetching ECL source for workunit {0}", wuid || localize("(unspecified)"))
83+
: localize("Cannot fetch: HPCC Platform not connected"),
84+
confirmationMessages: connected ? undefined : {
85+
title: localize("HPCC Platform not connected"),
86+
message: new vscode.MarkdownString(localize("This tool requires an active HPCC connection.")),
87+
}
88+
};
89+
}
90+
}

src/ecl/lm/tools/getWorkunitErrors.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@ interface FormattedException {
2222
}
2323

2424
export interface IGetWorkunitErrorsParameters {
25-
/**
26-
* Workunit ID (WUID) to fetch errors and warnings for
27-
*/
2825
wuid: string;
2926
}
3027

@@ -86,15 +83,13 @@ export class GetWorkunitErrorsTool implements vscode.LanguageModelTool<IGetWorku
8683
const opts = await createServiceOptions(session);
8784

8885
try {
89-
// Attach to the workunit and fetch its details
9086
const wu = Workunit.attach(opts, wuid);
9187
await wu.refresh();
9288

9389
throwIfCancellationRequested(token);
9490

9591
const parts: vscode.LanguageModelTextPart[] = [];
9692

97-
// Add workunit basic state information
9893
const detailsUrl = session.wuDetailsUrl(wu.Wuid);
9994
parts.push(new vscode.LanguageModelTextPart(localize("Errors/Warnings for Workunit {0}:", wuid)));
10095

@@ -106,7 +101,6 @@ export class GetWorkunitErrorsTool implements vscode.LanguageModelTool<IGetWorku
106101
);
107102
parts.push(new vscode.LanguageModelTextPart(summary));
108103

109-
// Fetch and add exceptions (errors and warnings)
110104
const exceptions = await wu.fetchECLExceptions().catch(() => []);
111105
throwIfCancellationRequested(token);
112106

0 commit comments

Comments
 (0)