Skip to content

Commit

Permalink
feat: dc with api plugin generator & template update (#12104)
Browse files Browse the repository at this point in the history
* feat: dc with api plugin generator

* refactor: more

* refactor: template

* refactor: templates

* refactor: templates

* refactor: templates

* refactor: templates

* test: ut

* test: ut

* refactor: more

* refactor: more

* refactor: format

* refactor: pr comment

* refactor: minor
  • Loading branch information
yuqizhou77 authored Jul 30, 2024
1 parent 2b1b7ed commit 64f90bb
Show file tree
Hide file tree
Showing 31 changed files with 754 additions and 262 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

/**
* @author [email protected]
*/

import { Context, FxError, Inputs, ok, Result } from "@microsoft/teamsfx-api";
import { DefaultTemplateGenerator } from "../templates/templateGenerator";
import {
ApiAuthOptions,
ApiPluginStartOptions,
CapabilityOptions,
ProgrammingLanguage,
QuestionNames,
} from "../../../question";
import { ActionContext } from "../../middleware/actionExecutionMW";
import { Generator } from "../generator";
import { merge } from "lodash";
import { TemplateNames } from "../templates/templateNames";
import { TemplateInfo } from "../templates/templateInfo";

const enum telemetryProperties {
templateName = "template-name",
isDeclarativeCopilot = "is-declarative-copilot",
}

export class CopilotExtensionFromScratchGenerator extends DefaultTemplateGenerator {
componentName = "copilot-extension-from-scratch-generator";
public activate(context: Context, inputs: Inputs): boolean {
return (
(inputs[QuestionNames.Capabilities] === CapabilityOptions.declarativeCopilot().id ||
inputs[QuestionNames.Capabilities] === CapabilityOptions.apiPlugin().id) &&
inputs[QuestionNames.ApiPluginType] === ApiPluginStartOptions.newApi().id
);
}

public getTemplateInfos(
context: Context,
inputs: Inputs,
destinationPath: string,
actionContext?: ActionContext
): Promise<Result<TemplateInfo[], FxError>> {
const auth = inputs[QuestionNames.ApiAuth];
const appName = inputs[QuestionNames.AppName];
const language = inputs[QuestionNames.ProgrammingLanguage] as ProgrammingLanguage;
const safeProjectNameFromVS =
language === "csharp" ? inputs[QuestionNames.SafeProjectName] : undefined;
const isDeclarativeCopilot =
inputs[QuestionNames.Capabilities] === CapabilityOptions.declarativeCopilot().id;

const replaceMap = {
...Generator.getDefaultVariables(
appName,
safeProjectNameFromVS,
inputs.targetFramework,
inputs.placeProjectFileInSolutionDir === "true"
),
DeclarativeCopilot: isDeclarativeCopilot ? "true" : "",
};

const filterFn = (fileName: string) => {
if (fileName.includes("repairDeclarativeCopilot.json")) {
return isDeclarativeCopilot;
} else {
return true;
}
};

const templateName =
auth === ApiAuthOptions.apiKey().id
? TemplateNames.ApiPluginFromScratchBearer
: auth === ApiAuthOptions.oauth().id
? TemplateNames.ApiPluginFromScratchOAuth
: TemplateNames.ApiPluginFromScratch;

merge(actionContext?.telemetryProps, {
[telemetryProperties.templateName]: templateName,
[telemetryProperties.isDeclarativeCopilot]: isDeclarativeCopilot.toString(),
});

return Promise.resolve(
ok([
{
templateName,
language: language,
replaceMap,
filterFn,
},
])
);
}
}
2 changes: 2 additions & 0 deletions packages/fx-core/src/component/generator/generatorProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { SpecGenerator } from "./apiSpec/generator";
import { CopilotExtensionFromScratchGenerator } from "./copilotExtensionFromScratch/generator";
import { OfficeAddinGeneratorNew } from "./officeAddin/generator";
import { SPFxGeneratorImport, SPFxGeneratorNew } from "./spfx/spfxGenerator";
import { SsrTabGenerator } from "./templates/ssrTabGenerator";
Expand All @@ -14,4 +15,5 @@ export const Generators = [
new SPFxGeneratorNew(),
new SPFxGeneratorImport(),
new SpecGenerator(),
new CopilotExtensionFromScratchGenerator(),
];
Original file line number Diff line number Diff line change
Expand Up @@ -345,14 +345,6 @@ export const inputsToTemplateName: Map<{ [key: string]: any }, TemplateNames> =
TemplateNames.CustomCopilotAssistantAssistantsApi,
],
// Copilot Plugin
[
{
[QuestionNames.Capabilities]: CapabilityOptions.apiPlugin().id,
[QuestionNames.ApiPluginType]: ApiPluginStartOptions.newApi().id,
[QuestionNames.ApiAuth]: ApiAuthOptions.none().id,
},
TemplateNames.ApiPluginFromScratch,
],
[
{
[QuestionNames.Capabilities]: CapabilityOptions.apiPlugin().id,
Expand All @@ -376,12 +368,4 @@ export const inputsToTemplateName: Map<{ [key: string]: any }, TemplateNames> =
},
TemplateNames.BasicGpt,
],
[
{
[QuestionNames.Capabilities]: CapabilityOptions.declarativeCopilot().id,
[QuestionNames.WithPlugin]: DeclarativeCopilotTypeOptions.withPlugin().id,
[QuestionNames.ApiPluginType]: ApiPluginStartOptions.newApi().id,
},
TemplateNames.GptWithPluginFromScratch,
],
]);
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

/**
* @author [email protected]
*/

import { Inputs, Platform } from "@microsoft/teamsfx-api";
import { assert } from "chai";
import "mocha";
import { createContext } from "../../../src/common/globalVars";
import {
ApiAuthOptions,
ApiPluginStartOptions,
CapabilityOptions,
QuestionNames,
} from "../../../src/question";
import { CopilotExtensionFromScratchGenerator } from "../../../src/component/generator/copilotExtensionFromScratch/generator";
describe("apiPluginFromScratch", async () => {
describe("activate and get template name", async () => {
it("api plugin", async () => {
const generator = new CopilotExtensionFromScratchGenerator();
const context = createContext();
const inputs: Inputs = {
platform: Platform.CLI,
projectPath: "./",
[QuestionNames.Capabilities]: CapabilityOptions.apiPlugin().id,
[QuestionNames.ApiPluginType]: ApiPluginStartOptions.newApi().id,
[QuestionNames.ApiAuth]: ApiAuthOptions.none().id,
[QuestionNames.AppName]: "app",
};
let res = await generator.activate(context, inputs);
let info = await generator.getTemplateInfos(context, inputs, ".");
assert.isTrue(res);
assert.equal(info.isOk() && info.value[0].templateName, "api-plugin-from-scratch");

inputs[QuestionNames.ApiAuth] = ApiAuthOptions.apiKey().id;
res = await generator.activate(context, inputs);
info = await generator.getTemplateInfos(context, inputs, ".");
assert.isTrue(res);
assert.equal(info.isOk() && info.value[0].templateName, "api-plugin-from-scratch-bearer");

inputs[QuestionNames.ApiAuth] = ApiAuthOptions.oauth().id;
res = await generator.activate(context, inputs);
info = await generator.getTemplateInfos(context, inputs, ".");
assert.isTrue(res);
assert.equal(info.isOk() && info.value[0].templateName, "api-plugin-from-scratch-oauth");
if (info.isOk()) {
const filterFn = info.value[0].filterFn;
assert.isFalse(filterFn?.("repairDeclarativeCopilot.json"));
assert.isTrue(filterFn?.("test.json"));
}
});

it("declarative Copilot", async () => {
const generator = new CopilotExtensionFromScratchGenerator();
const context = createContext();
const inputs: Inputs = {
platform: Platform.CLI,
projectPath: "./",
[QuestionNames.Capabilities]: CapabilityOptions.declarativeCopilot().id,
[QuestionNames.ApiPluginType]: ApiPluginStartOptions.newApi().id,
[QuestionNames.ApiAuth]: ApiAuthOptions.none().id,
[QuestionNames.AppName]: "app",
};
let res = await generator.activate(context, inputs);
let info = await generator.getTemplateInfos(context, inputs, ".");
assert.isTrue(res);
assert.equal(info.isOk() && info.value[0].templateName, "api-plugin-from-scratch");

inputs[QuestionNames.ApiAuth] = ApiAuthOptions.apiKey().id;
res = await generator.activate(context, inputs);
info = await generator.getTemplateInfos(context, inputs, ".");
assert.isTrue(res);
assert.equal(info.isOk() && info.value[0].templateName, "api-plugin-from-scratch-bearer");

inputs[QuestionNames.ApiAuth] = ApiAuthOptions.oauth().id;
res = await generator.activate(context, inputs);
info = await generator.getTemplateInfos(context, inputs, ".");
assert.isTrue(res);
assert.equal(info.isOk() && info.value[0].templateName, "api-plugin-from-scratch-oauth");

if (info.isOk()) {
const filterFn = info.value[0].filterFn;
assert.isTrue(filterFn?.("repairDeclarativeCopilot.json"));
assert.isTrue(filterFn?.("test.json"));
}
});
});
});
11 changes: 9 additions & 2 deletions templates/common/api-plugin-existing-api/README.md.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ When you extend Copilot for Microsoft 365, you maximize the efficiency of your a

With the declarative copilot, you can build a custom version of Copilot that can be used for specific scenarios, such as for specialized knowledge, implementing specific processes, or simply to save time by reusing a set of AI prompts. For example, a grocery shopping Copilot declarative copilot can be used to create a grocery list based on a meal plan that you send to Copilot.

You can extend declarative copilots using plugins to retrieve data and execute tasks on external systems. A Declarative copilot can utilize multiple plugins at the same time.
You can extend declarative copilots using plugins to retrieve data and execute tasks on external systems. A declarative copilot can utilize multiple plugins at the same time.
{{/DeclarativeCopilot}}

## Get started with the template
Expand All @@ -39,8 +39,15 @@ You can extend declarative copilots using plugins to retrieve data and execute t
2. In the Account section, sign in with your [Microsoft 365 account](https://docs.microsoft.com/microsoftteams/platform/toolkit/accounts) if you haven't already.
3. Create Teams app by clicking `Provision` in "Lifecycle" section.
4. Select `Preview in Copilot (Edge)` or `Preview in Copilot (Chrome)` from the launch configuration dropdown.
5. Open the `Copilot` app and send a prompt to trigger your plugin.
{{^DeclarativeCopilot}}
5. Open the `Copilot` app.
> Note: Please make sure to switch to New Teams when Teams web client has launched
6. In the message compose area, select the `Plugins` icon, then enable the plugin.
7. Send a prompt to trigger the plugin.
{{/DeclarativeCopilot}}
{{#DeclarativeCopilot}}
5. Select your declarative Copilot from the `Copilot` app and send a prompt.
{{/DeclarativeCopilot}}

{{#ApiKey}}
> [!NOTE]
Expand Down
Binary file modified templates/common/api-plugin-existing-api/appPackage/color.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified templates/common/api-plugin-existing-api/appPackage/outline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified templates/common/copilot-gpt-basic/appPackage/color.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified templates/common/copilot-gpt-basic/appPackage/outline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified templates/csharp/message-extension-copilot/appPackage/color.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified templates/csharp/message-extension-copilot/appPackage/outline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
120 changes: 0 additions & 120 deletions templates/js/api-plugin-from-scratch/.vscode/launch.json

This file was deleted.

Loading

0 comments on commit 64f90bb

Please sign in to comment.