Skip to content

Commit

Permalink
perf(ai-generator): update scaffolding for csharp ai generator (#12492)
Browse files Browse the repository at this point in the history
* perf(ai-generator): update scaffolding for csharp ai generator

* perf: add test case

* perf: add test case

---------

Co-authored-by: rentu <rentu>
  • Loading branch information
SLdragon authored Oct 8, 2024
1 parent be7db5f commit 98764a1
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 5 deletions.
14 changes: 13 additions & 1 deletion packages/fx-core/src/component/generator/apiSpec/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,19 @@ export class SpecGenerator extends DefaultTemplateGenerator {
const openapiSpecFileName = getTemplateInfosState.isYaml
? DefaultApiSpecYamlFileName
: DefaultApiSpecJsonFileName;
const openapiSpecPath = path.join(apiSpecFolderPath, openapiSpecFileName);

let openapiSpecPath = path.join(apiSpecFolderPath, openapiSpecFileName);

if (getTemplateInfosState.templateName === forCustomCopilotRagCustomApi) {
const language = inputs[QuestionNames.ProgrammingLanguage] as ProgrammingLanguage;
if (language === ProgrammingLanguage.CSharp) {
openapiSpecPath = path.join(
destinationPath,
DefaultApiSpecFolderName,
openapiSpecFileName
);
}
}

await fs.ensureDir(apiSpecFolderPath);

Expand Down
17 changes: 14 additions & 3 deletions packages/fx-core/src/component/generator/apiSpec/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1036,7 +1036,12 @@ function parseSpec(spec: OpenAPIV3.Document): [SpecObject[], boolean] {
return [res, needAuth];
}

const commonLanguages = [ProgrammingLanguage.TS, ProgrammingLanguage.JS, ProgrammingLanguage.PY];
const commonLanguages = [
ProgrammingLanguage.TS,
ProgrammingLanguage.JS,
ProgrammingLanguage.PY,
ProgrammingLanguage.CSharp,
];

async function updatePromptForCustomApi(
spec: OpenAPIV3.Document,
Expand All @@ -1058,7 +1063,10 @@ async function updateAdaptiveCardForCustomApi(
destinationPath: string
): Promise<void> {
if (commonLanguages.includes(language as ProgrammingLanguage)) {
const adaptiveCardsFolderPath = path.join(destinationPath, "src", "adaptiveCards");
let adaptiveCardsFolderPath = path.join(destinationPath, "src", "adaptiveCards");
if (language === ProgrammingLanguage.CSharp) {
adaptiveCardsFolderPath = path.join(destinationPath, "adaptiveCards");
}
await fs.ensureDir(adaptiveCardsFolderPath);

for (const item of specItems) {
Expand Down Expand Up @@ -1358,7 +1366,10 @@ export async function updateForCustomApi(
destinationPath: string,
openapiSpecFileName: string
): Promise<void> {
const chatFolder = path.join(destinationPath, "src", "prompts", "chat");
let chatFolder = path.join(destinationPath, "src", "prompts", "chat");
if (language === ProgrammingLanguage.CSharp) {
chatFolder = path.join(destinationPath, "prompts", "Chat");
}
await fs.ensureDir(chatFolder);

// 1. update prompt folder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import mockedEnv, { RestoreFn } from "mocked-env";
import { FeatureFlagName } from "../../../src/common/featureFlags";
import * as commonUtils from "../../../src/common/utils";
import * as helper from "../../../src/component/generator/apiSpec/helper";
import { fail } from "assert";

const teamsManifest: TeamsAppManifest = {
name: {
Expand Down Expand Up @@ -826,6 +827,18 @@ describe("updateForCustomApi", async () => {
expect(data).not.to.contains("{{");
expect(data).not.to.contains("# Replace with action code");
}

if (file.toString().endsWith("actions.json")) {
expect(file == path.join("path", "prompts", "Chat", "actions.json")).to.be.true;
}

if (file.toString().endsWith("skprompt.txt")) {
expect(file == path.join("path", "prompts", "Chat", "skprompt.txt")).to.be.true;
}

if (file.toString().endsWith("getHello.json")) {
expect(file == path.join("path", "adaptiveCards", "getHello.json")).to.be.true;
}
});

sandbox
Expand All @@ -834,6 +847,32 @@ describe("updateForCustomApi", async () => {
await CopilotPluginHelper.updateForCustomApi(spec, "csharp", "path", "openapi.yaml");
});

it("unknown language: unknown", async () => {
sandbox.stub(fs, "ensureDir").resolves();
sandbox.stub(fs, "writeFile").callsFake((file, data) => {
if (file == path.join("path", "APIActions.cs")) {
fail("actions.json should not be created for unknown language");
}

if (file.toString().endsWith("actions.json")) {
fail("actions.json should not be created for unknown language");
}

if (file.toString().endsWith("skprompt.txt")) {
fail("actions.json should not be created for unknown language");
}

if (file.toString().endsWith("getHello.json")) {
fail("actions.json should not be created for unknown language");
}
});

sandbox
.stub(fs, "readFile")
.resolves(Buffer.from("test code // Replace with action code {{OPENAPI_SPEC_PATH}}"));
await CopilotPluginHelper.updateForCustomApi(spec, "unknown", "path", "openapi.yaml");
});

it("happy path with spec without path", async () => {
const limitedSpec = {
openapi: "3.0.0",
Expand Down Expand Up @@ -2265,6 +2304,58 @@ describe("SpecGenerator", async () => {
assert.isTrue(generateBasedOnSpec.calledOnce);
});

it("generateCustomCopilot for csharp: success", async () => {
const inputs: Inputs = {
platform: Platform.VSCode,
projectPath: "path",
[QuestionNames.ProgrammingLanguage]: ProgrammingLanguage.CSharp,
[QuestionNames.ApiSpecLocation]: "test.yaml",
[QuestionNames.ApiOperation]: ["operation1"],
getTemplateInfosState: {
templateName: "custom-copilot-rag-custom-api",
isPlugin: false,
uri: "https://test.com",
isYaml: false,
type: ProjectType.TeamsAi,
},
};
const context = createContext();
sandbox
.stub(SpecParser.prototype, "validate")
.resolves({ status: ValidationStatus.Valid, errors: [], warnings: [] });
sandbox.stub(SpecParser.prototype, "getFilteredSpecs").resolves([
{
openapi: "3.0.0",
info: {
title: "test",
version: "1.0",
},
paths: {},
},
{
openapi: "3.0.0",
info: {
title: "test",
version: "1.0",
},
paths: {},
},
]);
sandbox.stub(CopilotPluginHelper, "updateForCustomApi").resolves();
sandbox.stub(fs, "ensureDir").resolves();
sandbox.stub(manifestUtils, "_readAppManifest").resolves(ok(teamsManifest));
const generateBasedOnSpec = sandbox
.stub(SpecParser.prototype, "generate")
.resolves({ allSuccess: true, warnings: [] });
sandbox.stub(pluginGeneratorHelper, "generateScaffoldingSummary").resolves("");

const generator = new SpecGenerator();
const result = await generator.post(context, inputs, "projectPath");

assert.isTrue(result.isOk());
assert.isTrue(generateBasedOnSpec.calledOnce);
});

it("generateCustomCopilot: CLI with warning", async () => {
const inputs: Inputs = {
platform: Platform.CLI,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ builder.Services.AddTransient<IBot>(sp =>
PromptFolder = "./Prompts"
});

prompts.AddFunction("get_actions", async (context, memory, functions, tokenizer, args) =>
prompts.AddFunction("getAction", async (context, memory, functions, tokenizer, args) =>
{
var skpromptContent = File.ReadAllText("./Prompts/chat/actions.json");
return await Task.FromResult(skpromptContent);
Expand Down

0 comments on commit 98764a1

Please sign in to comment.