Skip to content

Commit 3f7717f

Browse files
Merge pull request #1802 from OfficeDev/v-hrajandira/bot-knowledge-hub-agent-AzureOpenAi
New Sample - Agent Knowledge Hub (NodeJS and C#) - Azure Open AI
2 parents 93e76f9 + 7bd8bac commit 3f7717f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+5771
-0
lines changed

.github/workflows/build-complete-samples.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,10 @@ jobs:
496496
name: 'bot-auth0-adaptivecard'
497497
version: '6.0.x'
498498

499+
- project_path: 'samples/agent-knowledge-hub/csharp/AgentKnowledgeHub/AgentKnowledgeHub.csproj'
500+
name: 'agent-knowledge-hub'
501+
version: '8.0.x'
502+
499503
- project_path: 'samples/bot-shared-channel-events/csharp/SharedChannelEvents/SharedChannelEvents.csproj'
500504
name: 'bot-shared-channel-events'
501505
version: '6.0.x'
@@ -1079,6 +1083,10 @@ jobs:
10791083
name: 'tab-adhoccalls-transcript-recording-api-server'
10801084
version: '18.x'
10811085

1086+
- project_path: 'samples/agent-knowledge-hub/nodejs'
1087+
name: 'agent-knowledge-hub'
1088+
version: '18.x'
1089+
10821090
fail-fast: false
10831091
name: Build all "${{ matrix.name }}" nodejs
10841092
defaults:
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.14.36603.0
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgentKnowledgeHub", "AgentKnowledgeHub\AgentKnowledgeHub.csproj", "{BB5E7FD1-6F35-390F-3531-0983AF30B78A}"
7+
EndProject
8+
Project("{A9E3F50B-275E-4AF7-ADCE-8BE12D41E305}") = "M365Agent", "M365Agent\M365Agent.atkproj", "{B069B3BD-F6BC-CC40-82AB-3FCC2EA50FDF}"
9+
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}"
11+
ProjectSection(SolutionItems) = preProject
12+
AgentKnowledgeHub.slnLaunch.user = AgentKnowledgeHub.slnLaunch.user
13+
EndProjectSection
14+
EndProject
15+
Global
16+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
17+
Debug|Any CPU = Debug|Any CPU
18+
Release|Any CPU = Release|Any CPU
19+
EndGlobalSection
20+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
21+
{BB5E7FD1-6F35-390F-3531-0983AF30B78A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
22+
{BB5E7FD1-6F35-390F-3531-0983AF30B78A}.Debug|Any CPU.Build.0 = Debug|Any CPU
23+
{BB5E7FD1-6F35-390F-3531-0983AF30B78A}.Release|Any CPU.ActiveCfg = Release|Any CPU
24+
{BB5E7FD1-6F35-390F-3531-0983AF30B78A}.Release|Any CPU.Build.0 = Release|Any CPU
25+
{B069B3BD-F6BC-CC40-82AB-3FCC2EA50FDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26+
{B069B3BD-F6BC-CC40-82AB-3FCC2EA50FDF}.Debug|Any CPU.Build.0 = Debug|Any CPU
27+
{B069B3BD-F6BC-CC40-82AB-3FCC2EA50FDF}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
28+
{B069B3BD-F6BC-CC40-82AB-3FCC2EA50FDF}.Release|Any CPU.ActiveCfg = Release|Any CPU
29+
{B069B3BD-F6BC-CC40-82AB-3FCC2EA50FDF}.Release|Any CPU.Build.0 = Release|Any CPU
30+
{B069B3BD-F6BC-CC40-82AB-3FCC2EA50FDF}.Release|Any CPU.Deploy.0 = Release|Any CPU
31+
EndGlobalSection
32+
GlobalSection(SolutionProperties) = preSolution
33+
HideSolutionNode = FALSE
34+
EndGlobalSection
35+
GlobalSection(ExtensibilityGlobals) = postSolution
36+
SolutionGuid = {D48688EA-80DF-4655-B2B2-F1BFF444698D}
37+
EndGlobalSection
38+
EndGlobal
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# TeamsFx files
2+
build
3+
appPackage/build
4+
env/.env.*.user
5+
env/.env.local
6+
appsettings.Development.json
7+
appsettings.Playground.json
8+
.deployment
9+
10+
# User-specific files
11+
*.user
12+
13+
# Build results
14+
[Dd]ebug/
15+
[Dd]ebugPublic/
16+
[Rr]elease/
17+
[Rr]eleases/
18+
x64/
19+
x86/
20+
bld/
21+
[Bb]in/
22+
[Oo]bj/
23+
[Ll]og/
24+
25+
# Notification local store
26+
.notification.localstore.json
27+
.notification.playgroundstore.json
28+
29+
# devTools
30+
devTools/
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using Microsoft.Bot.Builder.TraceExtensions;
2+
using Microsoft.Bot.Schema;
3+
using Microsoft.Teams.AI;
4+
5+
namespace AgentKnowledgeHub
6+
{
7+
public class AdapterWithErrorHandler : TeamsAdapter
8+
{
9+
public AdapterWithErrorHandler(IConfiguration auth, ILogger<TeamsAdapter> logger)
10+
: base(auth, null, logger)
11+
{
12+
OnTurnError = async (turnContext, exception) =>
13+
{
14+
// Log any leaked exception from the application.
15+
// NOTE: In production environment, you should consider logging this to
16+
// Azure Application Insights. Visit https://aka.ms/bottelemetry to see how
17+
// to add telemetry capture to your agent.
18+
logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");
19+
20+
// Only send error message for user messages, not for other message types so the agent doesn't spam a channel or chat.
21+
if (turnContext.Activity.Type == ActivityTypes.Message)
22+
{
23+
// Send a message to the user
24+
await turnContext.SendActivityAsync($"The agent encountered an unhandled error: {exception.Message}");
25+
await turnContext.SendActivityAsync("To continue to run this agent, please fix the agent source code.");
26+
27+
// Send a trace activity
28+
await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError");
29+
}
30+
};
31+
}
32+
}
33+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<PackageReference Include="Azure.Identity" Version="1.13.1" />
10+
<PackageReference Include="Microsoft.Bot.Builder" Version="4.22.9" />
11+
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Version="4.22.9" />
12+
<PackageReference Include="Microsoft.Teams.AI" Version="1.8.*" />
13+
<PackageReference Include="System.Text.Json" Version="8.0.5" />
14+
</ItemGroup>
15+
16+
<ItemGroup>
17+
<Content Include="Prompts\chat\instructions.txt">
18+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
19+
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
20+
</Content>
21+
</ItemGroup>
22+
23+
<!-- Exclude local settings from publish -->
24+
<ItemGroup>
25+
<Content Remove="appsettings.Development.json" />
26+
<Content Include="appsettings.Development.json">
27+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
28+
<CopyToPublishDirectory>None</CopyToPublishDirectory>
29+
</Content>
30+
<Content Remove="appsettings.Playground.json" />
31+
<Content Include="appsettings.Playground.json">
32+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
33+
<CopyToPublishDirectory>None</CopyToPublishDirectory>
34+
</Content>
35+
</ItemGroup>
36+
</Project>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace AgentKnowledgeHub
2+
{
3+
public class ConfigOptions
4+
{
5+
public string BOT_ID { get; set; }
6+
public string BOT_PASSWORD { get; set; }
7+
public string BOT_TYPE { get; set; }
8+
public string BOT_TENANT_ID { get; set; }
9+
public AzureConfigOptions Azure { get; set; }
10+
}
11+
12+
/// <summary>
13+
/// Options for Azure OpenAI and Azure Content Safety
14+
/// </summary>
15+
public class AzureConfigOptions
16+
{
17+
public string OpenAIApiKey { get; set; }
18+
public string OpenAIEndpoint { get; set; }
19+
public string OpenAIDeploymentName { get; set; }
20+
}
21+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
using Microsoft.Bot.Builder;
3+
using Microsoft.Teams.AI;
4+
5+
namespace AgentKnowledgeHub.Controllers
6+
{
7+
[Route("api/messages")]
8+
[ApiController]
9+
public class BotController : ControllerBase
10+
{
11+
private readonly TeamsAdapter Adapter;
12+
private readonly IBot Bot;
13+
14+
public BotController(TeamsAdapter adapter, IBot bot)
15+
{
16+
Adapter = adapter;
17+
Bot = bot;
18+
}
19+
20+
[HttpPost]
21+
public async Task PostAsync(CancellationToken cancellationToken = default)
22+
{
23+
await Adapter.ProcessAsync(Request, Response, Bot, cancellationToken);
24+
}
25+
}
26+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using Microsoft.Teams.AI.State;
2+
3+
namespace AgentKnowledgeHub.Models
4+
{
5+
// Extend the turn state by configuring custom strongly typed state classes.
6+
public class AppState : TurnState
7+
{
8+
public AppState()
9+
{
10+
ScopeDefaults[CONVERSATION_SCOPE] = new ConversationState();
11+
}
12+
13+
/// <summary>
14+
/// Stores all the conversation-related state.
15+
/// </summary>
16+
public new ConversationState Conversation
17+
{
18+
get
19+
{
20+
TurnStateEntry scope = GetScope(CONVERSATION_SCOPE);
21+
22+
if (scope == null)
23+
{
24+
throw new ArgumentException("TurnState hasn't been loaded. Call LoadStateAsync() first.");
25+
}
26+
27+
return (ConversationState)scope.Value!;
28+
}
29+
set
30+
{
31+
TurnStateEntry scope = GetScope(CONVERSATION_SCOPE);
32+
33+
if (scope == null)
34+
{
35+
throw new ArgumentException("TurnState hasn't been loaded. Call LoadStateAsync() first.");
36+
}
37+
38+
scope.Replace(value!);
39+
}
40+
}
41+
}
42+
43+
// This class adds custom properties to the turn state which will be accessible in the various handler methods.
44+
public class ConversationState : Record
45+
{}
46+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
using AgentKnowledgeHub;
2+
using AgentKnowledgeHub.Models;
3+
using Microsoft.Bot.Builder;
4+
using Microsoft.Bot.Builder.Integration.AspNet.Core;
5+
using Microsoft.Bot.Connector.Authentication;
6+
using Microsoft.Teams.AI;
7+
using Microsoft.Teams.AI.AI.Models;
8+
using Microsoft.Teams.AI.AI.Planners;
9+
using Microsoft.Teams.AI.AI.Prompts;
10+
using Microsoft.Teams.AI.State;
11+
using Microsoft.Teams.AI.AI;
12+
13+
var builder = WebApplication.CreateBuilder(args);
14+
15+
builder.Services.AddControllers();
16+
builder.Services.AddHttpClient("WebClient", client => client.Timeout = TimeSpan.FromSeconds(600));
17+
builder.Services.AddHttpContextAccessor();
18+
19+
// Prepare Configuration for ConfigurationBotFrameworkAuthentication
20+
var config = builder.Configuration.Get<ConfigOptions>();
21+
builder.Configuration["MicrosoftAppType"] = config.BOT_TYPE;
22+
builder.Configuration["MicrosoftAppId"] = config.BOT_ID;
23+
builder.Configuration["MicrosoftAppPassword"] = config.BOT_PASSWORD;
24+
builder.Configuration["MicrosoftAppTenantId"] = config.BOT_TENANT_ID;
25+
// Create the Bot Framework Authentication to be used with the Bot Adapter.
26+
builder.Services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();
27+
28+
// Create the Cloud Adapter with error handling enabled.
29+
// Note: some classes expect a BotAdapter and some expect a BotFrameworkHttpAdapter, so
30+
// register the same adapter instance for both types.
31+
builder.Services.AddSingleton<TeamsAdapter, AdapterWithErrorHandler>();
32+
builder.Services.AddSingleton<IBotFrameworkHttpAdapter>(sp => sp.GetService<TeamsAdapter>());
33+
builder.Services.AddSingleton<BotAdapter>(sp => sp.GetService<TeamsAdapter>());
34+
35+
builder.Services.AddSingleton<IStorage, MemoryStorage>();
36+
37+
builder.Services.AddSingleton<OpenAIModel>(sp => new(
38+
new AzureOpenAIModelOptions(
39+
config.Azure.OpenAIApiKey,
40+
config.Azure.OpenAIDeploymentName,
41+
config.Azure.OpenAIEndpoint
42+
)
43+
{
44+
LogRequests = true,
45+
},
46+
sp.GetService<ILoggerFactory>()
47+
));
48+
49+
// Create the bot as transient. In this case the ASP Controller is expecting an IBot.
50+
builder.Services.AddTransient<IBot>(sp =>
51+
{
52+
// Create loggers
53+
ILoggerFactory loggerFactory = sp.GetService<ILoggerFactory>();
54+
55+
// Create Prompt Manager
56+
PromptManager prompts = new(new()
57+
{
58+
PromptFolder = "./Prompts"
59+
});
60+
61+
// Create ActionPlanner
62+
ActionPlanner<AppState> planner = new(
63+
options: new(
64+
model: sp.GetService<OpenAIModel>(),
65+
prompts: prompts,
66+
defaultPrompt: async (context, state, planner) =>
67+
{
68+
PromptTemplate template = prompts.GetPrompt("chat");
69+
return await Task.FromResult(template);
70+
}
71+
)
72+
{
73+
LogRepairs = true,
74+
},
75+
loggerFactory: loggerFactory
76+
);
77+
78+
AIOptions<AppState> options = new(planner);
79+
options.EnableFeedbackLoop = true;
80+
81+
Application<AppState> app = new ApplicationBuilder<AppState>()
82+
.WithAIOptions(options)
83+
.WithStorage(sp.GetService<IStorage>())
84+
.Build();
85+
86+
app.OnFeedbackLoop((turnContext, turnState, feedbackLoopData, _) =>
87+
{
88+
Console.WriteLine($"Your feedback is {turnContext.Activity.Value.ToString()}");
89+
return Task.CompletedTask;
90+
});
91+
92+
return app;
93+
});
94+
95+
var app = builder.Build();
96+
97+
if (app.Environment.IsDevelopment())
98+
{
99+
app.UseDeveloperExceptionPage();
100+
}
101+
102+
app.UseStaticFiles();
103+
app.UseRouting();
104+
app.MapControllers();
105+
106+
app.Run();
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"schema": 1.1,
3+
"description": "A bot that can chat with users",
4+
"type": "completion",
5+
"completion": {
6+
"completion_type": "chat",
7+
"include_history": true,
8+
"include_input": true,
9+
"max_input_tokens": 2800,
10+
"max_tokens": 1000,
11+
"temperature": 0.9,
12+
"top_p": 0.0,
13+
"presence_penalty": 0.6,
14+
"frequency_penalty": 0.0
15+
}
16+
}

0 commit comments

Comments
 (0)