From cf3c429f2beb87250abf65ae8895fbc975aaec83 Mon Sep 17 00:00:00 2001 From: Gordon Smith Date: Tue, 2 Dec 2025 11:53:43 +0000 Subject: [PATCH] feat: add AI tool to fetch ECL Archive Signed-off-by: Gordon Smith --- package.json | 28 +++++++++ src/ecl/lm/tools.ts | 2 + src/ecl/lm/tools/getWorkunitECL.ts | 90 +++++++++++++++++++++++++++ src/ecl/lm/tools/getWorkunitErrors.ts | 6 -- 4 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 src/ecl/lm/tools/getWorkunitECL.ts diff --git a/package.json b/package.json index e42a593..e087374 100644 --- a/package.json +++ b/package.json @@ -271,6 +271,34 @@ ] } }, + { + "name": "ecl-extension-getWorkunitECL", + "tags": [ + "workunit", + "ecl", + "archive", + "source", + "code", + "ecl-extension" + ], + "toolReferenceName": "getWorkunitECL", + "displayName": "Get Workunit ECL / ECL Archive", + "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).", + "canBeReferencedInPrompt": true, + "icon": "$(code)", + "inputSchema": { + "type": "object", + "properties": { + "wuid": { + "type": "string", + "description": "The Workunit ID (WUID) to fetch ECL source for" + } + }, + "required": [ + "wuid" + ] + } + }, { "name": "ecl-extension-syntaxCheck", "tags": [ diff --git a/src/ecl/lm/tools.ts b/src/ecl/lm/tools.ts index 77fed12..50957c1 100644 --- a/src/ecl/lm/tools.ts +++ b/src/ecl/lm/tools.ts @@ -1,6 +1,7 @@ import * as vscode from "vscode"; import { FindWorkunitsTool } from "./tools/findWorkunits"; import { GetWorkunitErrorsTool } from "./tools/getWorkunitErrors"; +import { GetWorkunitECLTool } from "./tools/getWorkunitECL"; import { FindLogicalFilesTool } from "./tools/findLogicalFiles"; import { SyntaxCheckTool } from "./tools/syntaxCheck"; @@ -11,6 +12,7 @@ export class ECLLMTools { protected constructor(ctx: vscode.ExtensionContext) { ctx.subscriptions.push(vscode.lm.registerTool("ecl-extension-findWorkunits", new FindWorkunitsTool())); ctx.subscriptions.push(vscode.lm.registerTool("ecl-extension-getWorkunitErrors", new GetWorkunitErrorsTool())); + ctx.subscriptions.push(vscode.lm.registerTool("ecl-extension-getWorkunitECL", new GetWorkunitECLTool())); ctx.subscriptions.push(vscode.lm.registerTool("ecl-extension-findLogicalFiles", new FindLogicalFilesTool())); ctx.subscriptions.push(vscode.lm.registerTool("ecl-extension-syntaxCheck", new SyntaxCheckTool())); diff --git a/src/ecl/lm/tools/getWorkunitECL.ts b/src/ecl/lm/tools/getWorkunitECL.ts new file mode 100644 index 0000000..f9bfffb --- /dev/null +++ b/src/ecl/lm/tools/getWorkunitECL.ts @@ -0,0 +1,90 @@ +import * as vscode from "vscode"; +import { Workunit } from "@hpcc-js/comms"; +import { isPlatformConnected } from "../../../hpccplatform/session"; +import { reporter } from "../../../telemetry"; +import localize from "../../../util/localize"; +import { createServiceOptions, logToolEvent, requireConnectedSession, throwIfCancellationRequested } from "../utils/index"; + +export interface IGetWorkunitECLParameters { + wuid: string; +} + +export class GetWorkunitECLTool implements vscode.LanguageModelTool { + async invoke(options: vscode.LanguageModelToolInvocationOptions, token: vscode.CancellationToken) { + reporter?.sendTelemetryEvent("lmTool.invoke", { tool: "getWorkunitECL" }); + const params = options.input; + + const wuid = typeof params.wuid === "string" ? params.wuid.trim() : ""; + if (wuid.length === 0) { + throw new vscode.LanguageModelError(localize("WUID is required"), { cause: "invalid_parameters" }); + } + + logToolEvent("getWorkunitECL", "invoke start", { wuid }); + + const session = requireConnectedSession(); + const opts = await createServiceOptions(session); + + try { + const wu = Workunit.attach(opts, wuid); + await wu.refresh(); + + throwIfCancellationRequested(token); + + const parts: vscode.LanguageModelTextPart[] = []; + + const detailsUrl = session.wuDetailsUrl(wu.Wuid); + parts.push(new vscode.LanguageModelTextPart(localize("ECL Source for Workunit {0}:", wuid))); + + const summary = localize( + "Workunit {0} on {1} ({2})", + wu.Wuid, + wu.Cluster || localize("unknown cluster"), + wu.State || localize("unknown state") + ); + parts.push(new vscode.LanguageModelTextPart(summary)); + if (detailsUrl) { + parts.push(new vscode.LanguageModelTextPart(`${localize("ECL Watch URL:")} ${detailsUrl}`)); + } + + throwIfCancellationRequested(token); + const eclArchive = await wu.fetchArchive().catch(() => ""); + + if (eclArchive) { + parts.push(new vscode.LanguageModelTextPart(localize("\nECL Archive (XML format):"))); + parts.push(new vscode.LanguageModelTextPart("```xml\n" + eclArchive + "\n```")); + } else { + parts.push(new vscode.LanguageModelTextPart(localize("No ECL archive available for this workunit."))); + } + + logToolEvent("getWorkunitECL", "invoke success", { + wuid: wu.Wuid, + state: wu.State, + hasECL: !!eclArchive, + }); + + return new vscode.LanguageModelToolResult(parts); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + logToolEvent("getWorkunitECL", "invoke failed", { wuid, error: errorMessage }); + throw new vscode.LanguageModelError( + localize("Failed to fetch workunit ECL: {0}", errorMessage), + { cause: error } + ); + } + } + + async prepareInvocation(options: vscode.LanguageModelToolInvocationPrepareOptions, _token: vscode.CancellationToken) { + const connected = isPlatformConnected(); + const wuid = typeof options.input.wuid === "string" ? options.input.wuid.trim() : ""; + + return { + invocationMessage: connected + ? localize("Fetching ECL source for workunit {0}", wuid || localize("(unspecified)")) + : localize("Cannot fetch: HPCC Platform not connected"), + confirmationMessages: connected ? undefined : { + title: localize("HPCC Platform not connected"), + message: new vscode.MarkdownString(localize("This tool requires an active HPCC connection.")), + } + }; + } +} diff --git a/src/ecl/lm/tools/getWorkunitErrors.ts b/src/ecl/lm/tools/getWorkunitErrors.ts index 7d890db..f03ce62 100644 --- a/src/ecl/lm/tools/getWorkunitErrors.ts +++ b/src/ecl/lm/tools/getWorkunitErrors.ts @@ -22,9 +22,6 @@ interface FormattedException { } export interface IGetWorkunitErrorsParameters { - /** - * Workunit ID (WUID) to fetch errors and warnings for - */ wuid: string; } @@ -86,7 +83,6 @@ export class GetWorkunitErrorsTool implements vscode.LanguageModelTool []); throwIfCancellationRequested(token);