Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: expose command to create Declarative Agent with API Spec #13137

Merged
merged 9 commits into from
Feb 8, 2025
1 change: 1 addition & 0 deletions packages/vscode-extension/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,7 @@
"teamstoolkit.walkthroughs.buildIntelligentApps.intelligentAppResources.description": "Explore these resources to build intelligent apps and enhance your development projects\n🗒️ [Generative AI for Beginners](https://github.com/microsoft/generative-ai-for-beginners/tree/main)\n✨ [Retrieval Augmented Generation (RAG)](https://learn.microsoft.com/en-us/azure/search/retrieval-augmented-generation-overview)\n📚 [AI Learning and Community Hub](https://learn.microsoft.com/en-us/ai/)",
"teamstoolkit.m365.needSignIn.message": "You need to sign in your Microsoft 365 account.",
"teamstoolkit.handler.createPluginWithManifest.error.missingParameter": "Invalid parameter in the createPluginWithManifest command. Usage: createPluginWithManifest(API_SPEC_PATH:string, PLUGIN_MANIFEST_PATH: string, { lastCommand: string }, OUTPUT_FOLDER?: string). Valid values for LAST_COMMAND: createPluginWithManifest, createDeclarativeCopilotWithManifest.",
"teamstoolkit.handler.createDeclarativeAgentWithApiSpec.error.invalidParameter": "Invalid parameter in the createDeclarativeAgentWithApiSpec command. Usage: createDeclarativeAgentWithApiSpec(API_SPEC_PATH: string).",
"teamstoolkit.error.KiotaNotInstalled": "You need to install Microsoft Kiota extension with minimum version %s to use this feature.",
"teamstoolkit.handeler.addAuthConfig.notification": "Teams Toolkit has successfully updated your project configuration (teamsapp.yaml and teamsapp.local.yaml) files with added action to support authentication flow. You can proceed to remote provision.",
"teamstoolkit.handeler.addAuthConfig.notification.provision": "Provision"
Expand Down
7 changes: 7 additions & 0 deletions packages/vscode-extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ import { manifestListener } from "./manifestListener";
import { onSwitchAzureTenant, onSwitchM365Tenant } from "./handlers/accounts/switchTenantHandler";
import { kiotaRegenerate } from "./handlers/kiotaRegenerateHandler";
import { releaseControlledFeatureSettings } from "./releaseBasedFeatureSettings";
import { createDeclarativeAgentWithApiSpec } from "./handlers/createDeclarativeAgentWithApiSpecHandler";

export async function activate(context: vscode.ExtensionContext) {
const value = IsChatParticipantEnabled && semver.gte(vscode.version, "1.90.0");
Expand Down Expand Up @@ -554,6 +555,12 @@ function registerInternalCommands(context: vscode.ExtensionContext) {

registerInCommandController(context, CommandKeys.SigninAzure, signinAzureCallback);

const createDeclarativeAgentWithApiSpecCommand = vscode.commands.registerCommand(
"fx-extension.createDeclarativeAgentWithApiSpec",
(...args) => Correlator.run(createDeclarativeAgentWithApiSpec, args)
);
context.subscriptions.push(createDeclarativeAgentWithApiSpecCommand);

// Register createPluginWithManifest command
if (featureFlagManager.getBooleanValue(FeatureFlags.KiotaIntegration)) {
const createPluginWithManifestCommand = vscode.commands.registerCommand(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import {
CreateProjectResult,
err,
FxError,
ok,
Result,
Stage,
UserError,
} from "@microsoft/teamsfx-api";
import { getSystemInputs } from "../utils/systemEnvUtils";
import {
ApiPluginStartOptions,
CapabilityOptions,
ProjectTypeOptions,
QuestionNames,
} from "@microsoft/teamsfx-core";
import { runCommand } from "./sharedOpts";
import * as vscode from "vscode";
import { openFolder } from "../utils/workspaceUtils";
import { ExtensionSource } from "../error/error";
import { ExtTelemetry } from "../telemetry/extTelemetry";
import { getTriggerFromProperty } from "../utils/telemetryUtils";
import {
TelemetryEvent,
TelemetryProperty,
TelemetrySuccess,
} from "../telemetry/extTelemetryEvents";
import { localize } from "../utils/localizeUtils";

export async function createDeclarativeAgentWithApiSpec(
args?: any[]
): Promise<Result<any, FxError>> {
ExtTelemetry.sendTelemetryEvent(
TelemetryEvent.CreateDeclarativeAgentWithApiSpecStart,
getTriggerFromProperty(args)
);
if (!args || args.length !== 1 || !args[0] || typeof args[0] !== "string") {
const error = new UserError(
ExtensionSource,
"invalidParameter",
localize("teamstoolkit.handler.createDeclarativeAgentWithApiSpec.error.invalidParameter")
);
ExtTelemetry.sendTelemetryErrorEvent(TelemetryEvent.CreateDeclarativeAgentWithApiSpec, error);
return err(error);
}

const specPath = args[0];

const inputs = getSystemInputs();
inputs[QuestionNames.ApiSpecLocation] = specPath;
inputs[QuestionNames.ApiPluginType] = ApiPluginStartOptions.apiSpec().id;
inputs.capabilities = CapabilityOptions.declarativeAgent().id;
inputs[QuestionNames.WithPlugin] = "yes";
inputs[QuestionNames.ProjectType] = ProjectTypeOptions.Agent().id;

const result = await runCommand(Stage.create, inputs);

if (result.isErr()) {
ExtTelemetry.sendTelemetryErrorEvent(
TelemetryEvent.CreateDeclarativeAgentWithApiSpec,
result.error
);
return err(result.error);
}

const res = result.value as CreateProjectResult;
const projectPathUri = vscode.Uri.file(res.projectPath);
await openFolder(projectPathUri, true, res.warnings);
ExtTelemetry.sendTelemetryEvent(TelemetryEvent.CreateDeclarativeAgentWithApiSpec, {
[TelemetryProperty.Success]: TelemetrySuccess.Yes,
});

return ok({});
}
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,9 @@ export enum TelemetryEvent {
CreatePluginWithManifestStart = "create-plugin-with-manifest-start",
CreatePluginWithManifest = "create-plugin-with-manifest",

CreateDeclarativeAgentWithApiSpecStart = "create-declarative-agent-with-api-spec-start",
CreateDeclarativeAgentWithApiSpec = "create-declarative-agent-with-api-spec",

InstallKiota = "install-kiota",
Configuration = "vsc-configuration",

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as sinon from "sinon";
import * as chai from "chai";
import * as globalVariables from "../../src/globalVariables";
import * as telemetry from "../../src/telemetry/extTelemetry";
import * as workspaceUtils from "../../src/utils/workspaceUtils";
import { createDeclarativeAgentWithApiSpec } from "../../src/handlers/createDeclarativeAgentWithApiSpecHandler";
import { err, UserError } from "@microsoft/teamsfx-api";
import { MockCore } from "../mocks/mockCore";

describe("createDeclarativeAgentWithApiSpecHandler", () => {
const sandbox = sinon.createSandbox();

beforeEach(() => {
sandbox.stub(telemetry.ExtTelemetry, "sendTelemetryEvent");
sandbox.stub(telemetry.ExtTelemetry, "sendTelemetryErrorEvent");
});

afterEach(() => {
sandbox.restore();
});

it("should return error if args are invalid", async () => {
const core = new MockCore();
sandbox.stub(globalVariables, "core").value(core);
const openFolder = sandbox.stub(workspaceUtils, "openFolder").resolves();

const res = await createDeclarativeAgentWithApiSpec([]);

chai.assert.isTrue(res.isErr());
chai.assert.isTrue(openFolder.notCalled);
if (res.isErr()) {
chai.assert.equal(res.error.name, "invalidParameter");
}
});

it("should create project successfully with valid args", async () => {
const core = new MockCore();
sandbox.stub(globalVariables, "core").value(core);
const openFolder = sandbox.stub(workspaceUtils, "openFolder").resolves();

const res = await createDeclarativeAgentWithApiSpec(["test-path"]);

chai.assert.isTrue(res.isOk());
chai.assert.isTrue(openFolder.calledOnce);
});

it("should throw error if core return error", async () => {
const core = new MockCore();
sandbox.stub(globalVariables, "core").value(core);
sandbox
.stub(globalVariables.core, "createProject")
.resolves(err(new UserError("core", "fakeError", "fakeErrorMessage")));
const openFolder = sandbox.stub(workspaceUtils, "openFolder").resolves();

const res = await createDeclarativeAgentWithApiSpec(["test-path"]);

chai.assert.isTrue(res.isErr());
chai.assert.isTrue(openFolder.notCalled);
if (res.isErr()) {
chai.assert.equal(res.error.name, "fakeError");
}
});
});
Loading