diff --git a/packages/cli/src/commands/models/create.ts b/packages/cli/src/commands/models/create.ts index b72571c25b..c8b9fb8b02 100644 --- a/packages/cli/src/commands/models/create.ts +++ b/packages/cli/src/commands/models/create.ts @@ -45,13 +45,7 @@ function adjustOptions(options: CLICommandOption[]) { break; } } - if (!featureFlagManager.getBooleanValue(FeatureFlags.CustomizeGpt)) { - //skip customize GPT questions if customize GPT is not enabled. - const customizeGptQuestionNames = [QuestionNames.CustomizeGptWithPluginStart]; - options = options.filter( - (option) => !customizeGptQuestionNames.includes(option.name as QuestionNames) - ); - } + return options; } diff --git a/packages/cli/tests/unit/commands.tests.ts b/packages/cli/tests/unit/commands.tests.ts index 7c109492c8..1cfc98e8f4 100644 --- a/packages/cli/tests/unit/commands.tests.ts +++ b/packages/cli/tests/unit/commands.tests.ts @@ -106,31 +106,6 @@ describe("CLI commands", () => { assert.isTrue(res.isOk()); }); - it("createProjectOptions - need to adjust options when feature flag is enabled", async () => { - mockedEnvRestore = mockedEnv({ - DEVELOP_COPILOT_PLUGIN: "true", - API_COPILOT_PLUGIN: "false", - [FeatureFlags.CustomizeGpt.name]: "true", - }); - sandbox.stub(activate, "getFxCore").returns(new FxCore({} as any)); - sandbox.stub(FxCore.prototype, "createProject").resolves(ok({ projectPath: "..." })); - - const ctx: CLIContext = { - command: { ...getCreateCommand(), fullName: "new" }, - optionValues: {}, - globalOptionValues: {}, - argumentValues: [], - telemetryProperties: {}, - }; - - const filteredQuestionNames = [QuestionNames.CustomizeGptWithPluginStart.toString()]; - assert.isTrue( - ctx.command.options?.filter((o) => filteredQuestionNames.includes(o.name)).length === 1 - ); - const res = await getCreateCommand().handler!(ctx); - assert.isTrue(res.isOk()); - }); - it("core return error", async () => { sandbox.stub(activate, "getFxCore").returns(new FxCore({} as any)); sandbox.stub(FxCore.prototype, "createProject").resolves(err(new UserCancelError())); diff --git a/packages/fx-core/src/common/constants.ts b/packages/fx-core/src/common/constants.ts index 7c97464f65..1e3bc47056 100644 --- a/packages/fx-core/src/common/constants.ts +++ b/packages/fx-core/src/common/constants.ts @@ -52,5 +52,5 @@ export class FeatureFlagName { static readonly ChatParticipant = "TEAMSFX_CHAT_PARTICIPANT"; static readonly NewGenerator = "TEAMSFX_NEW_GENERATOR"; static readonly CopilotAuth = "TEAMSFX_COPILOT_AUTH"; - static readonly CustomizeGpt = "TEAMSFX_CUSTOMIZE_GPT"; + static readonly CustomizeGpt = "TEAMSFX_DECLARATIVE_COPILOT"; } diff --git a/packages/fx-core/src/component/coordinator/index.ts b/packages/fx-core/src/component/coordinator/index.ts index d3d191d27a..315fdf3f3d 100644 --- a/packages/fx-core/src/component/coordinator/index.ts +++ b/packages/fx-core/src/component/coordinator/index.ts @@ -201,11 +201,7 @@ class Coordinator { const res = await OfficeAddinGenerator.generate(context, inputs, projectPath); if (res.isErr()) return err(res.error); } - } else if ( - capability === CapabilityOptions.copilotPluginApiSpec().id || - inputs[QuestionNames.CustomizeGptWithPluginStart] === - CapabilityOptions.copilotPluginApiSpec().id - ) { + } else if (capability === CapabilityOptions.copilotPluginApiSpec().id) { const res = await CopilotPluginGenerator.generatePluginFromApiSpec( context, inputs, diff --git a/packages/fx-core/src/component/generator/copilotPlugin/generator.ts b/packages/fx-core/src/component/generator/copilotPlugin/generator.ts index c3057620f0..f08c9aca11 100644 --- a/packages/fx-core/src/component/generator/copilotPlugin/generator.ts +++ b/packages/fx-core/src/component/generator/copilotPlugin/generator.ts @@ -19,7 +19,6 @@ import { ResponseTemplatesFolderName, AppPackageFolderName, Warning, - ApiOperation, AuthInfo, SystemError, } from "@microsoft/teamsfx-api"; @@ -44,7 +43,7 @@ import { } from "./helper"; import { getLocalizedString } from "../../../common/localizeUtils"; import { manifestUtils } from "../../driver/teamsApp/utils/ManifestUtils"; -import { CapabilityOptions, ProgrammingLanguage } from "../../../question/create"; +import { ProgrammingLanguage } from "../../../question/create"; import * as fs from "fs-extra"; import { assembleError } from "../../../error"; import { @@ -53,14 +52,10 @@ import { ValidationStatus, WarningType, ProjectType, - Utils, - ParseOptions, } from "@microsoft/m365-spec-parser"; import * as util from "util"; import { isValidHttpUrl } from "../../../question/util"; import { merge } from "lodash"; -import { TemplateNames } from "../templates/templateNames"; -import { copilotGptManifestUtils } from "../../driver/teamsApp/utils/CopilotGptManifestUtils"; import { isCopilotAuthEnabled } from "../../../common/featureFlags"; const fromApiSpecComponentName = "copilot-plugin-existing-api"; @@ -74,7 +69,6 @@ const apiSpecFolderName = "apiSpecificationFile"; const apiSpecYamlFileName = "openapi.yaml"; const apiSpecJsonFileName = "openapi.json"; const pluginManifestFileName = "ai-plugin.json"; -const defaultPluginId = "plugin_1"; const copilotPluginExistingApiSpecUrlTelemetryEvent = "copilot-plugin-existing-api-spec-url"; @@ -142,11 +136,7 @@ export class CopilotPluginGenerator { destinationPath: string, actionContext?: ActionContext ): Promise> { - const templateName = - inputs[QuestionNames.CustomizeGptWithPluginStart] === - CapabilityOptions.copilotPluginApiSpec().id - ? TemplateNames.BasicGpt - : apiPluginFromApiSpecTemplateName; + const templateName = apiPluginFromApiSpecTemplateName; const componentName = fromApiSpecComponentName; merge(actionContext?.telemetryProps, { [telemetryProperties.templateName]: templateName }); @@ -294,14 +284,10 @@ export class CopilotPluginGenerator { }); // validate API spec - const isGptPlugin = templateName === TemplateNames.BasicGpt; const specParser = new SpecParser( url, isPlugin - ? { - ...copilotPluginParserOptions, - isGptPlugin, - } + ? copilotPluginParserOptions : { allowBearerTokenAuth: true, // Currently, API key auth support is actually bearer token auth allowMultipleParameters: true, @@ -414,25 +400,6 @@ export class CopilotPluginGenerator { if (updateManifestRes.isErr()) return err(updateManifestRes.error); } - // update gpt.json including plugins - if (isGptPlugin && teamsManifest.copilotGpts && teamsManifest.copilotGpts.length > 0) { - const copilotGptPath = path.join( - destinationPath, - AppPackageFolderName, - teamsManifest.copilotGpts[0].file - ); - await fs.ensureFile(copilotGptPath); - const addPluginRes = await copilotGptManifestUtils.addPlugin( - copilotGptPath, - defaultPluginId, - pluginManifestFileName - ); - - if (addPluginRes.isErr()) { - return err(addPluginRes.error); - } - } - if (componentName === forCustomCopilotRagCustomApi) { const specs = await specParser.getFilteredSpecs(filters); const spec = specs[1]; diff --git a/packages/fx-core/src/question/create.ts b/packages/fx-core/src/question/create.ts index 967f9f650c..e0954582fc 100644 --- a/packages/fx-core/src/question/create.ts +++ b/packages/fx-core/src/question/create.ts @@ -864,17 +864,18 @@ export class CapabilityOptions { // customize GPT static customizeGptBasic(): OptionItem { return { - id: "customize-gpt-basic", - label: "Basic GPT", - detail: "A simple GPT skeleton you can author without any plugin", + id: "basic-declarative-copilot", + label: "Basic Declarative Copilot", + detail: "A declarative Copilot skeleton you can author without any plugin", }; } static customizeGptWithPlugin(): OptionItem { return { - id: "customize-gpt-with-plugin", - label: "GPT with a plugin", - detail: "A GPT containing a Copilot plugin", + id: "declarative-copilot-with-plugin-from-scratch", + label: "Declarative Copilot with a plugin using Azure Functions", + detail: + "A declarative Copilot containing a Copilot plugin with a new API from Azure Functions", }; } } @@ -921,7 +922,7 @@ export function capabilityQuestion(): SingleSelectQuestion { case ProjectTypeOptions.customCopilot().id: return getLocalizedString("core.createProjectQuestion.projectType.customCopilot.title"); case ProjectTypeOptions.customizeGpt().id: - return "Choose the GPT type"; + return "Choose Declarative Copilot type"; default: return getLocalizedString("core.createCapabilityQuestion.titleNew"); } @@ -1416,23 +1417,6 @@ export function SPFxImportFolderQuestion(hasDefaultFunc = false): FolderQuestion }; } -function CustomizeGptWithPluginStartQuestion(): SingleSelectQuestion { - return { - name: QuestionNames.CustomizeGptWithPluginStart, - title: getLocalizedString("core.createProjectQuestion.projectType.copilotPlugin.title"), - type: "singleSelect", - staticOptions: GptWithPluginStartOptions(), - cliDescription: "Copilot Plugin.", - placeholder: getLocalizedString( - "core.createProjectQuestion.projectType.copilotPlugin.placeholder" - ), - }; -} - -function GptWithPluginStartOptions(): OptionItem[] { - return [CapabilityOptions.copilotPluginNewApi(), CapabilityOptions.copilotPluginApiSpec()]; -} - export function officeAddinHostingQuestion(): SingleSelectQuestion { return { name: QuestionNames.OfficeAddinHost, @@ -2505,11 +2489,6 @@ export function capabilitySubTree(): IQTreeNode { }, ], }, - // Customize GPT with plugin - { - condition: { equals: CapabilityOptions.customizeGptWithPlugin().id }, - data: CustomizeGptWithPluginStartQuestion(), - }, { // Search ME sub-tree condition: { equals: CapabilityOptions.m365SearchMe().id }, @@ -2520,8 +2499,6 @@ export function capabilitySubTree(): IQTreeNode { condition: (inputs: Inputs) => { return ( inputs[QuestionNames.Capabilities] === CapabilityOptions.copilotPluginApiSpec().id || - inputs[QuestionNames.CustomizeGptWithPluginStart] === - CapabilityOptions.copilotPluginApiSpec().id || inputs[QuestionNames.Capabilities] === CapabilityOptions.copilotPluginOpenAIPlugin().id || inputs[QuestionNames.MeArchitectureType] === MeArchitectureOptions.apiSpec().id @@ -2589,8 +2566,6 @@ export function capabilitySubTree(): IQTreeNode { inputs[QuestionNames.Capabilities] !== CapabilityOptions.copilotPluginOpenAIPlugin().id && inputs[QuestionNames.Capabilities] !== CapabilityOptions.customizeGptBasic().id && - inputs[QuestionNames.CustomizeGptWithPluginStart] !== - CapabilityOptions.copilotPluginApiSpec().id && inputs[QuestionNames.MeArchitectureType] !== MeArchitectureOptions.apiSpec().id && inputs[QuestionNames.Capabilities] !== CapabilityOptions.officeAddinImport().id && inputs[QuestionNames.Capabilities] !== CapabilityOptions.outlookAddinImport().id diff --git a/packages/fx-core/src/question/inputs/CreateProjectInputs.ts b/packages/fx-core/src/question/inputs/CreateProjectInputs.ts index 50e885768a..e1f8b4deec 100644 --- a/packages/fx-core/src/question/inputs/CreateProjectInputs.ts +++ b/packages/fx-core/src/question/inputs/CreateProjectInputs.ts @@ -45,8 +45,8 @@ export interface CreateProjectInputs extends Inputs { | "message-extension" | "BotAndMessageExtension" | "TabNonSsoAndBot" - | "customize-gpt-basic" - | "customize-gpt-with-plugin" + | "basic-declarative-copilot" + | "declarative-copilot-with-plugin-from-scratch" | "json-taskpane" | "office-content-addin" | "word-taskpane" @@ -80,8 +80,6 @@ export interface CreateProjectInputs extends Inputs { "spfx-webpart-name"?: string; /** @description SPFx solution folder */ "spfx-folder"?: string; - /** @description Copilot Plugin */ - "customize-gpt-with-plugin-start"?: "copilot-plugin-new-api" | "copilot-plugin-existing-api"; /** @description Architecture of Search Based Message Extension */ "me-architecture"?: "new-api" | "api-spec" | "bot-plugin" | "bot"; /** @description OpenAPI Description Document */ diff --git a/packages/fx-core/src/question/options/CreateProjectOptions.ts b/packages/fx-core/src/question/options/CreateProjectOptions.ts index 3e2648436e..e7d13854a9 100644 --- a/packages/fx-core/src/question/options/CreateProjectOptions.ts +++ b/packages/fx-core/src/question/options/CreateProjectOptions.ts @@ -53,8 +53,8 @@ export const CreateProjectOptions: CLICommandOption[] = [ "message-extension", "BotAndMessageExtension", "TabNonSsoAndBot", - "customize-gpt-basic", - "customize-gpt-with-plugin", + "basic-declarative-copilot", + "declarative-copilot-with-plugin-from-scratch", "json-taskpane", "office-content-addin", "word-taskpane", @@ -122,12 +122,6 @@ export const CreateProjectOptions: CLICommandOption[] = [ type: "string", description: "Directory or Path that contains the existing SharePoint Framework solution.", }, - { - name: "customize-gpt-with-plugin-start", - type: "string", - description: "Copilot Plugin.", - choices: ["copilot-plugin-new-api", "copilot-plugin-existing-api"], - }, { name: "me-architecture", type: "string", diff --git a/packages/fx-core/src/question/questionNames.ts b/packages/fx-core/src/question/questionNames.ts index 438e016839..fec71b60b4 100644 --- a/packages/fx-core/src/question/questionNames.ts +++ b/packages/fx-core/src/question/questionNames.ts @@ -78,7 +78,6 @@ export enum QuestionNames { collaborationAppType = "collaborationType", DestinationApiSpecFilePath = "destination-api-spec-location", - CustomizeGptWithPluginStart = "customize-gpt-with-plugin-start", } export enum CliQuestionName { diff --git a/packages/fx-core/tests/component/generator/copilotPluginGenerator.test.ts b/packages/fx-core/tests/component/generator/copilotPluginGenerator.test.ts index 20bdb7b338..be6ac2deee 100644 --- a/packages/fx-core/tests/component/generator/copilotPluginGenerator.test.ts +++ b/packages/fx-core/tests/component/generator/copilotPluginGenerator.test.ts @@ -442,103 +442,6 @@ describe("copilotPluginGenerator", function () { assert.isTrue(updateManifestBasedOnOpenAIPlugin.calledOnce); }); - it("success if adding plugin for GPT basic", async function () { - const inputs: Inputs = { - platform: Platform.VSCode, - projectPath: "path", - [QuestionNames.Capabilities]: CapabilityOptions.customizeGptWithPlugin().id, - [QuestionNames.CustomizeGptWithPluginStart]: CapabilityOptions.copilotPluginApiSpec().id, - [QuestionNames.ApiSpecLocation]: "https://test.com", - [QuestionNames.ApiOperation]: ["operation1"], - supportedApisFromApiSpec: apiOperations, - }; - const context = createContextV3(); - sandbox - .stub(SpecParser.prototype, "validate") - .resolves({ status: ValidationStatus.Valid, errors: [], warnings: [] }); - sandbox.stub(fs, "ensureDir").resolves(); - sandbox.stub(fs, "ensureFile").resolves(); - sandbox.stub(manifestUtils, "_readAppManifest").resolves( - ok({ - ...teamsManifest, - copilotGpts: [ - { - id: "1", - file: "test", - }, - ], - }) - ); - sandbox.stub(CopilotPluginHelper, "isYamlSpecFile").resolves(false); - sandbox.stub(copilotGptManifestUtils, "addPlugin").resolves(ok({} as any)); - const generateBasedOnSpec = sandbox - .stub(SpecParser.prototype, "generateForCopilot") - .resolves({ allSuccess: true, warnings: [] }); - const getDefaultVariables = sandbox.stub(Generator, "getDefaultVariables").resolves(undefined); - const downloadTemplate = sandbox.stub(Generator, "generateTemplate").resolves(ok(undefined)); - - const result = await CopilotPluginGenerator.generatePluginFromApiSpec( - context, - inputs, - "projectPath" - ); - - assert.isTrue(result.isOk()); - assert.isTrue(getDefaultVariables.calledOnce); - assert.isTrue(downloadTemplate.calledOnce); - assert.isTrue(generateBasedOnSpec.calledOnce); - assert.equal(downloadTemplate.args[0][2], TemplateNames.BasicGpt); - }); - - it("error if adding plugin for GPT basic", async function () { - const inputs: Inputs = { - platform: Platform.VSCode, - projectPath: "path", - [QuestionNames.Capabilities]: CapabilityOptions.customizeGptWithPlugin().id, - [QuestionNames.CustomizeGptWithPluginStart]: CapabilityOptions.copilotPluginApiSpec().id, - [QuestionNames.ApiSpecLocation]: "https://test.com", - [QuestionNames.ApiOperation]: ["operation1"], - supportedApisFromApiSpec: apiOperations, - }; - const context = createContextV3(); - sandbox - .stub(SpecParser.prototype, "validate") - .resolves({ status: ValidationStatus.Valid, errors: [], warnings: [] }); - sandbox.stub(fs, "ensureDir").resolves(); - sandbox.stub(manifestUtils, "_readAppManifest").resolves( - ok({ - ...teamsManifest, - copilotGpts: [ - { - id: "1", - file: "test", - }, - ], - }) - ); - sandbox.stub(CopilotPluginHelper, "isYamlSpecFile").resolves(false); - sandbox.stub(fs, "ensureFile").resolves(); - sandbox - .stub(copilotGptManifestUtils, "addPlugin") - .resolves(err(new SystemError("testSource", "testName", "", ""))); - sandbox - .stub(SpecParser.prototype, "generateForCopilot") - .resolves({ allSuccess: true, warnings: [] }); - sandbox.stub(Generator, "getDefaultVariables").resolves(undefined); - sandbox.stub(Generator, "generateTemplate").resolves(ok(undefined)); - - const result = await CopilotPluginGenerator.generatePluginFromApiSpec( - context, - inputs, - "projectPath" - ); - - assert.isTrue(result.isErr()); - if (result.isErr()) { - assert.equal(result.error.source, "testSource"); - } - }); - it("failed to download template generator", async function () { const inputs: Inputs = { platform: Platform.VSCode, @@ -630,7 +533,7 @@ describe("copilotPluginGenerator", function () { const inputs: Inputs = { platform: Platform.VSCode, projectPath: "path", - [QuestionNames.ApiSpecLocation]: "https://test.com", + [QuestionNames.ApiSpecLocation]: "test.yaml", [QuestionNames.ApiOperation]: ["operation1"], }; const context = createContextV3(); diff --git a/packages/fx-core/tests/core/FxCore.test.ts b/packages/fx-core/tests/core/FxCore.test.ts index 111d3c8093..c12fbe2c7e 100644 --- a/packages/fx-core/tests/core/FxCore.test.ts +++ b/packages/fx-core/tests/core/FxCore.test.ts @@ -1489,7 +1489,6 @@ describe("getQuestions", async () => { "spfx-framework-type", "spfx-webpart-name", "spfx-folder", - "customize-gpt-with-plugin-start", "me-architecture", "openapi-spec-location", "api-operation", @@ -1531,7 +1530,6 @@ describe("getQuestions", async () => { "spfx-framework-type", "spfx-webpart-name", "spfx-folder", - "customize-gpt-with-plugin-start", "me-architecture", "openapi-spec-location", "api-operation", @@ -1573,7 +1571,6 @@ describe("getQuestions", async () => { "spfx-framework-type", "spfx-webpart-name", "spfx-folder", - "customize-gpt-with-plugin-start", "me-architecture", "openapi-spec-location", "api-operation", @@ -1616,7 +1613,6 @@ describe("getQuestions", async () => { "spfx-framework-type", "spfx-webpart-name", "spfx-folder", - "customize-gpt-with-plugin-start", "me-architecture", "openapi-spec-location", "api-operation", diff --git a/packages/fx-core/tests/question/create.test.ts b/packages/fx-core/tests/question/create.test.ts index 46581ab07c..9f99bfcf7f 100644 --- a/packages/fx-core/tests/question/create.test.ts +++ b/packages/fx-core/tests/question/create.test.ts @@ -3246,7 +3246,7 @@ describe("scaffold question", () => { assert.isTrue(options.length === 2); const title = typeof question.title === "function" ? await question.title(inputs) : question.title; - assert.equal(title, "Choose the GPT type"); + assert.equal(title, "Choose Declarative Copilot type"); return ok({ type: "success", result: CapabilityOptions.customizeGptBasic().id }); } else if (question.name === QuestionNames.AppName) { return ok({ type: "success", result: "test001" }); @@ -3291,12 +3291,6 @@ describe("scaffold question", () => { assert.isTrue(options.length === 2); return ok({ type: "success", result: CapabilityOptions.customizeGptWithPlugin().id }); - } else if (question.name === QuestionNames.CustomizeGptWithPluginStart) { - const select = question as SingleSelectQuestion; - const options = await select.staticOptions; - assert.isTrue(options.length === 2); - - return ok({ type: "success", result: CapabilityOptions.copilotPluginNewApi().id }); } else if (question.name === QuestionNames.ProgrammingLanguage) { return ok({ type: "success", result: "javascript" }); } else if (question.name === QuestionNames.AppName) { @@ -3310,68 +3304,11 @@ describe("scaffold question", () => { assert.deepEqual(questions, [ QuestionNames.ProjectType, QuestionNames.Capabilities, - QuestionNames.CustomizeGptWithPluginStart, QuestionNames.ProgrammingLanguage, QuestionNames.Folder, QuestionNames.AppName, ]); }); - - it("customize GPT with plugin from existing API", async () => { - const inputs: Inputs = { - platform: Platform.VSCode, - }; - const questions: string[] = []; - const visitor: QuestionTreeVisitor = async ( - question: Question, - ui: UserInteraction, - inputs: Inputs, - step?: number, - totalSteps?: number - ) => { - questions.push(question.name); - - await callFuncs(question, inputs); - - if (question.name === QuestionNames.ProjectType) { - const select = question as SingleSelectQuestion; - const options = await select.dynamicOptions!(inputs); - assert.isTrue(options.length === 6); - return ok({ type: "success", result: ProjectTypeOptions.customizeGpt().id }); - } else if (question.name === QuestionNames.Capabilities) { - const select = question as SingleSelectQuestion; - const options = await select.dynamicOptions!(inputs); - assert.isTrue(options.length === 2); - - return ok({ type: "success", result: CapabilityOptions.customizeGptWithPlugin().id }); - } else if (question.name === QuestionNames.CustomizeGptWithPluginStart) { - const select = question as SingleSelectQuestion; - const options = await select.staticOptions; - assert.isTrue(options.length === 2); - - return ok({ type: "success", result: CapabilityOptions.copilotPluginApiSpec().id }); - } else if (question.name === QuestionNames.ApiSpecLocation) { - return ok({ type: "success", result: "https://test.com" }); - } else if (question.name === QuestionNames.ApiOperation) { - return ok({ type: "success", result: ["testOperation1"] }); - } else if (question.name === QuestionNames.AppName) { - return ok({ type: "success", result: "test001" }); - } else if (question.name === QuestionNames.Folder) { - return ok({ type: "success", result: "./" }); - } - return ok({ type: "success", result: undefined }); - }; - await traverse(createProjectQuestionNode(), inputs, ui, undefined, visitor); - assert.deepEqual(questions, [ - QuestionNames.ProjectType, - QuestionNames.Capabilities, - QuestionNames.CustomizeGptWithPluginStart, - QuestionNames.ApiSpecLocation, - QuestionNames.ApiOperation, - QuestionNames.Folder, - QuestionNames.AppName, - ]); - }); }); });