Skip to content

Commit acc0210

Browse files
authored
feat: Teams AI CSharp batch 3 (#14579)
* refactor: template * refactor: template * refactor: template * refactor: more * refactor: more
1 parent 1fa7ff0 commit acc0210

32 files changed

+195
-146
lines changed

packages/fx-core/src/component/generator/openApiSpec/helper.ts

Lines changed: 75 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,14 +1203,14 @@ async function updatePromptForCustomApi(
12031203
): Promise<void> {
12041204
if (commonLanguages.includes(language as ProgrammingLanguage)) {
12051205
const object = `{ "path": null, "body": null, "query": null }`;
1206-
const cSharpObject = `{ "path": {}, "body": {}, "query": {} }`;
1206+
const cSharpObject = `{ "path": {}, "query": {} }`;
12071207
const promptFilePath = path.join(chatFolder, promptFileName);
12081208
const prompt = `The following is a conversation with an AI assistant.\nThe assistant can help to call APIs for the open api spec file${
12091209
spec.info.description ? ". " + spec.info.description : "."
12101210
}\nIf the API doesn't require parameters, invoke it with default JSON object ${
12111211
(language as ProgrammingLanguage) === ProgrammingLanguage.CSharp ? cSharpObject : object
12121212
}.\n\n${
1213-
shouldGenerateTeamsAIV2Code(language) ? "context:\nAvailable actions: {{getAction}}." : ""
1213+
shouldGenerateTeamsAIV2Code(language) ? "" : "context:\nAvailable actions: {{getAction}}."
12141214
}`;
12151215
await fs.writeFile(promptFilePath, prompt, { encoding: "utf-8", flag: "w" });
12161216
}
@@ -1306,7 +1306,11 @@ function filterSchema(schema: OpenAPIV3.SchemaObject): OpenAPIV3.SchemaObject {
13061306
}
13071307

13081308
function shouldGenerateTeamsAIV2Code(language: string) {
1309-
return language === ProgrammingLanguage.JS || language === ProgrammingLanguage.TS;
1309+
return (
1310+
language === ProgrammingLanguage.JS ||
1311+
language === ProgrammingLanguage.TS ||
1312+
language === ProgrammingLanguage.CSharp
1313+
);
13101314
}
13111315

13121316
async function updateActionForCustomApi(
@@ -1442,33 +1446,6 @@ async def {{operationId}}(
14421446
await context.send_activity(message)
14431447
return "success"
14441448
`,
1445-
cs: `
1446-
[Action("{{operationId}}")]
1447-
public async Task<string> {{functionName}}Async([ActionTurnContext] ITurnContext turnContext, [ActionTurnState] TurnState turnState, [ActionParameters] Dictionary<string, object> args)
1448-
{
1449-
try
1450-
{
1451-
RequestParams requestParam = ParseRequestParams(args);
1452-
1453-
var response = await Client.CallAsync("{{apiPath}}", Method.{{apiMethod}}, requestParam);
1454-
var data = response.Content;
1455-
1456-
var cardTemplatePath = "./adaptiveCards/{{operationId}}.json";
1457-
if (File.Exists(cardTemplatePath)) {
1458-
var message = RenderCardToMessage(cardTemplatePath, data);
1459-
await turnContext.SendActivityAsync(message);
1460-
}
1461-
else
1462-
{
1463-
await turnContext.SendActivityAsync(data);
1464-
}
1465-
}
1466-
catch (Exception ex) {
1467-
await turnContext.SendActivityAsync("Failed to call API with error: " + ex.Message);
1468-
}
1469-
1470-
return "complete";
1471-
}`,
14721449
};
14731450

14741451
const functionDefinitionCode = {
@@ -1500,6 +1477,12 @@ const functionDefinitionCode = {
15001477
}
15011478
}
15021479
)`,
1480+
cs: `
1481+
var {{operationId}}SchemaJson = FunctionDefinitionLoader.FunctionDefinitions["{{operationId}}"]["parameters"].ToJsonString();
1482+
JsonSchema {{operationId}}Schema = JsonSchema.FromText({{operationId}}SchemaJson);
1483+
prompt.Functions.Add(new Function(FunctionDefinitionLoader.FunctionDefinitions["{{operationId}}"]["name"].ToString(),
1484+
FunctionDefinitionLoader.FunctionDefinitions["{{operationId}}"]["description"].ToString(),
1485+
{{operationId}}Schema, (IDictionary<string, object?> args) => handlers.{{functionName}}(args).GetAwaiter().GetResult()));`,
15031486
};
15041487

15051488
const functionHandlerCode = {
@@ -1557,6 +1540,33 @@ module.exports = { {{operationId}}Handler };`,
15571540
}
15581541
15591542
};`,
1543+
cs: `
1544+
public async Task<string> {{functionName}}(IDictionary<string, object?> args)
1545+
{
1546+
try
1547+
{
1548+
RequestParams requestParam = ParseRequestParams(args);
1549+
var response = await Client.CallAsync("{{apiPath}}", Method.{{apiMethod}}, requestParam);
1550+
var data = response.Content;
1551+
1552+
var cardTemplatePath = "./adaptiveCards/{{operationId}}.json";
1553+
if (File.Exists(cardTemplatePath)) {
1554+
var card = RenderCard(cardTemplatePath, data);
1555+
await context.Send(new MessageActivity().AddAttachment(System.Text.Json.JsonSerializer.Deserialize<Microsoft.Teams.Cards.AdaptiveCard>(card)));
1556+
}
1557+
else
1558+
{
1559+
await context.Send(data);
1560+
}
1561+
}
1562+
catch (Exception ex)
1563+
{
1564+
Console.WriteLine(ex.Message);
1565+
}
1566+
1567+
return "results are shown already. completed.";
1568+
}
1569+
`,
15601570
};
15611571

15621572
const AuthCode = {
@@ -1639,23 +1649,47 @@ async function updateCodeForCustomApi(
16391649
.replace("# Replace with action code", actionsCode.join("\n"));
16401650
await fs.writeFile(botFilePath, updateBotFileContent);
16411651
} else if (language === ProgrammingLanguage.CSharp) {
1642-
const actionsCode = [];
1643-
const codeTemplate = ActionCode["cs"];
1652+
const functionDefinitionsCode = [];
1653+
const functionHandlersCode = [];
1654+
const functionDefinitionTemplate = functionDefinitionCode["cs"];
1655+
const functionHandlerTemplate = functionHandlerCode["cs"];
16441656
for (const item of specItems) {
1645-
const code = codeTemplate
1657+
functionDefinitionsCode.push(
1658+
functionDefinitionTemplate
1659+
.replace(/{{operationId}}/g, item.item.operationId!)
1660+
.replace(/{{functionName}}/g, Utils.updateFirstLetter(item.item.operationId!))
1661+
);
1662+
const code = functionHandlerTemplate
16461663
.replace(/{{operationId}}/g, item.item.operationId!)
16471664
.replace(/{{apiPath}}/g, item.pathUrl)
16481665
.replace(/{{apiMethod}}/g, Utils.updateFirstLetter(item.method))
16491666
.replace(/{{functionName}}/g, Utils.updateFirstLetter(item.item.operationId!));
1650-
actionsCode.push(code);
1667+
functionHandlersCode.push(code);
16511668
}
16521669

1653-
const apiActionCsFilePath = path.join(destinationPath, "APIActions.cs");
1654-
const apiActionCsFileContent = (await fs.readFile(apiActionCsFilePath)).toString();
1655-
const updateApiActionCsFileContent = apiActionCsFileContent
1656-
.replace("{{OPENAPI_SPEC_PATH}}", "apiSpecificationFile/" + openapiSpecFileName)
1657-
.replace("// Replace with action code", actionsCode.join("\n"));
1658-
await fs.writeFile(apiActionCsFilePath, updateApiActionCsFileContent);
1670+
const controllerFilePath = path.join(destinationPath, "Controllers", "Controller.cs");
1671+
const controllerFileContent = (await fs.readFile(controllerFilePath)).toString();
1672+
const updatedControllerFileContent = controllerFileContent.replace(
1673+
"// Replace with function definition code",
1674+
`${functionDefinitionsCode.join("\n")};`
1675+
);
1676+
await fs.writeFile(controllerFilePath, updatedControllerFileContent);
1677+
1678+
const handlerFilePath = path.join(destinationPath, "Functions", "Handlers.cs");
1679+
const handlerFileContent = (await fs.readFile(handlerFilePath)).toString();
1680+
const updatedHandlerFileContent = handlerFileContent.replace(
1681+
"// Replace with function handler code",
1682+
`${functionHandlersCode.join("\n")}`
1683+
);
1684+
await fs.writeFile(handlerFilePath, updatedHandlerFileContent);
1685+
1686+
const startFilePath = path.join(destinationPath, "Program.cs");
1687+
const startFileContent = (await fs.readFile(startFilePath)).toString();
1688+
const updatedStartFileContent = startFileContent.replace(
1689+
"{{OPENAPI_SPEC_PATH}}",
1690+
"apiSpecificationFile/" + openapiSpecFileName
1691+
);
1692+
await fs.writeFile(startFilePath, updatedStartFileContent);
16591693

16601694
const files = await fs.readdir(destinationPath);
16611695
const projectFileName = files.find((file) => file.endsWith(".csproj"));
@@ -1680,7 +1714,7 @@ export async function updateForCustomApi(
16801714
? path.join(destinationPath, "src", "app")
16811715
: path.join(destinationPath, "src", "prompts", "chat");
16821716
if (language === ProgrammingLanguage.CSharp) {
1683-
chatFolder = path.join(destinationPath, "prompts", "Chat");
1717+
chatFolder = path.join(destinationPath, "Functions");
16841718
}
16851719
await fs.ensureDir(chatFolder);
16861720

templates/vs/csharp/custom-copilot-basic/Controllers/Controller.cs.tpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using {{SafeProjectName}}.Models;
2+
using {{SafeProjectName}}.Utils;
23
using Microsoft.Teams.AI.Models.OpenAI;
34
using Microsoft.Teams.Api.Activities;
45
using Microsoft.Teams.Api.Activities.Invokes;
@@ -53,7 +54,6 @@ namespace {{SafeProjectName}}.Controllers
5354
await context.Send(welcomeText);
5455
}
5556
}
56-
5757
}
5858
}
5959
}

templates/vs/csharp/custom-copilot-basic/Program.cs.tpl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ using Microsoft.Teams.Plugins.AspNetCore.Extensions;
1414
var builder = WebApplication.CreateBuilder(args);
1515
var config = builder.Configuration.Get<ConfigOptions>();
1616

17+
if (config == null)
18+
{
19+
throw new InvalidOperationException("Missing configuration for ConfigOptions");
20+
}
21+
1722
Func<string[], string?, Task<ITokenResponse>> createTokenFactory = async (string[] scopes, string? tenantId) =>
1823
{
1924
var clientId = config.Teams.ClientId;

templates/vs/csharp/custom-copilot-basic/Utils/TextUtils.cs.tpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ using Microsoft.Teams.Api.Entities;
33

44
namespace {{SafeProjectName}}.Utils
55
{
6-
public static class Utils
6+
public static class TextUtils
77
{
88
public static string StripMentionsText(MessageActivity activity)
99
{

templates/vs/csharp/custom-copilot-basic/appsettings.Playground.json.tpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
}
1111
},
1212
"AllowedHosts": "*",
13-
"Teams": {
13+
"Teams": {
1414
"ClientId": "",
1515
"ClientSecret": "",
1616
"TenantId": "",

templates/vs/csharp/custom-copilot-basic/appsettings.json.tpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
}
1111
},
1212
"AllowedHosts": "*",
13-
"Teams": {
13+
"Teams": {
1414
"ClientId": "",
1515
"ClientSecret": "",
1616
"BotType": ""

templates/vs/csharp/custom-copilot-basic/{{ProjectName}}.csproj.tpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<PropertyGroup>
44
<TargetFramework>{{TargetFramework}}</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
67
</PropertyGroup>
78

89
{{^isNewProjectTypeEnabled}}

templates/vs/csharp/custom-copilot-rag-azure-ai-search/Controllers/Controller.cs.tpl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ namespace {{SafeProjectName}}.Controllers
5757
await context.Send(welcomeText);
5858
}
5959
}
60-
6160
}
6261
}
6362
}

templates/vs/csharp/custom-copilot-rag-azure-ai-search/Program.cs.tpl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ using Microsoft.Teams.Plugins.AspNetCore.Extensions;
1414
var builder = WebApplication.CreateBuilder(args);
1515
var config = builder.Configuration.Get<ConfigOptions>();
1616

17+
if (config == null)
18+
{
19+
throw new InvalidOperationException("Missing configuration for ConfigOptions");
20+
}
21+
1722
Func<string[], string?, Task<ITokenResponse>> createTokenFactory = async (string[] scopes, string? tenantId) =>
1823
{
1924
var clientId = config.Teams.ClientId;

templates/vs/csharp/custom-copilot-rag-azure-ai-search/appsettings.Playground.json.tpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
}
1111
},
1212
"AllowedHosts": "*",
13-
"Teams": {
13+
"Teams": {
1414
"ClientId": "",
1515
"ClientSecret": "",
1616
"BotType": ""

0 commit comments

Comments
 (0)