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

feat: dc with api plugin generator & template update #12104

Merged
merged 14 commits into from
Jul 30, 2024
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,
},
])
);
}
}
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
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
Loading