Skip to content

AdaptiveDialogBot as transient, and LanguageGenerationManager as singleton #6437

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

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using Microsoft.ApplicationInsights;
using Microsoft.Bot.Builder.ApplicationInsights;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Generators;
using Microsoft.Bot.Builder.Dialogs.Declarative.Resources;
using Microsoft.Bot.Builder.Dialogs.Memory;
using Microsoft.Bot.Builder.Dialogs.Memory.Scopes;
Expand Down Expand Up @@ -48,6 +49,53 @@ public ConfigurationAdaptiveDialogBot(
IEnumerable<IPathResolver> pathResolvers = default,
IEnumerable<Dialog> dialogs = default,
ILogger logger = null)
: this(
configuration,
resourceExplorer,
conversationState,
userState,
skillConversationIdFactoryBase,
languagePolicy,
new LanguageGeneratorManager(resourceExplorer),
botFrameworkAuthentication,
telemetryClient,
scopes,
pathResolvers,
dialogs,
logger)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="ConfigurationAdaptiveDialogBot"/> class using <see cref="IConfiguration"/>.
/// </summary>
/// <param name="configuration">An <see cref="IConfiguration"/> instance.</param>
/// <param name="resourceExplorer">The Bot Builder <see cref="ResourceExplorer"/> to load the <see cref="AdaptiveDialog"/> from.</param>
/// <param name="conversationState">The <see cref="ConversationState"/> implementation to use for this <see cref="AdaptiveDialog"/>.</param>
/// <param name="userState">The <see cref="UserState"/> implementation to use for this <see cref="AdaptiveDialog"/>.</param>
/// <param name="skillConversationIdFactoryBase">The <see cref="SkillConversationIdFactoryBase"/> implementation to use for this <see cref="AdaptiveDialog"/>.</param>
/// <param name="languagePolicy">The <see cref="LanguagePolicy"/> implementation to use for this <see cref="AdaptiveDialog"/>.</param>
/// <param name="languageGeneratorManager">A <see cref="LanguageGeneratorManager"/>.</param>
/// <param name="botFrameworkAuthentication">A <see cref="BotFrameworkAuthentication"/> for making calls to Bot Builder Skills.</param>
/// <param name="telemetryClient">A <see cref="IBotTelemetryClient"/> for logging bot telemetry events.</param>
/// <param name="scopes">A set of <see cref="MemoryScope"/> that will be added to the <see cref="ITurnContext"/>.</param>
/// <param name="pathResolvers">A set of <see cref="IPathResolver"/> that will be added to the <see cref="ITurnContext"/>.</param>
/// <param name="dialogs">Custom <see cref="Dialog"/> that will be added to the root DialogSet.</param>
/// <param name="logger">An <see cref="ILogger"/> instance.</param>
public ConfigurationAdaptiveDialogBot(
IConfiguration configuration,
ResourceExplorer resourceExplorer,
ConversationState conversationState,
UserState userState,
SkillConversationIdFactoryBase skillConversationIdFactoryBase,
LanguagePolicy languagePolicy,
LanguageGeneratorManager languageGeneratorManager,
BotFrameworkAuthentication botFrameworkAuthentication = null,
IBotTelemetryClient telemetryClient = null,
IEnumerable<MemoryScope> scopes = default,
IEnumerable<IPathResolver> pathResolvers = default,
IEnumerable<Dialog> dialogs = default,
ILogger logger = null)
: base(
configuration.GetSection(ConfigurationConstants.RootDialogKey).Value,
configuration.GetSection(ConfigurationConstants.LanguageGeneratorKey).Value ?? DefaultLanguageGeneratorId,
Expand All @@ -56,6 +104,7 @@ public ConfigurationAdaptiveDialogBot(
userState,
skillConversationIdFactoryBase,
languagePolicy,
languageGeneratorManager,
botFrameworkAuthentication ?? BotFrameworkAuthenticationFactory.Create(),
telemetryClient ?? new NullBotTelemetryClient(),
scopes ?? Enumerable.Empty<MemoryScope>(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Bot.Builder.ApplicationInsights;
using Microsoft.Bot.Builder.Azure.Blobs;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Generators;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Runtime.Component;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Runtime.Settings;
using Microsoft.Bot.Builder.Dialogs.Declarative.Resources;
Expand Down Expand Up @@ -89,10 +90,11 @@ public static void AddBotRuntime(this IServiceCollection services, IConfiguratio
services.TryAddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();

// IBot
services.TryAddSingleton<IBot, ConfigurationAdaptiveDialogBot>();
services.AddTransient<IBot, ConfigurationAdaptiveDialogBot>();

// Resource explorer
services.TryAddSingleton<ResourceExplorer, ConfigurationResourceExplorer>();
services.TryAddSingleton<LanguageGeneratorManager>();

// Language policy
services.TryAddSingleton<LanguagePolicy, ConfigurationLanguagePolicy>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Microsoft.Bot.Builder.Dialogs.Memory;
using Microsoft.Bot.Builder.Dialogs.Memory.Scopes;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Connector;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -40,7 +41,55 @@ public class AdaptiveDialogBot : IBot

private readonly Lazy<Task<Dialog>> _lazyRootDialog;
private readonly Lazy<LanguageGenerator> _lazyLanguageGenerator;
private readonly Lazy<LanguageGeneratorManager> _lazyLanguageGeneratorManager;
private readonly LanguageGeneratorManager _lazyLanguageGeneratorManager;

/// <summary>
/// Initializes a new instance of the <see cref="AdaptiveDialogBot"/> class. Creates LanguageGenerationManager using supplied ResourceExplorer.
/// </summary>
/// <param name="adaptiveDialogId">The id of the <see cref="AdaptiveDialog"/> to load from the <see cref="ResourceExplorer"/>.</param>
/// <param name="languageGeneratorId">The id of the <see cref="LanguageGenerator"/> to load from the <see cref="ResourceExplorer"/>.</param>
/// <param name="resourceExplorer">The Bot Builder <see cref="ResourceExplorer"/> to load the <see cref="Dialog"/> from.</param>
/// <param name="conversationState">A <see cref="ConversationState"/> implementation.</param>
/// <param name="userState">A <see cref="UserState"/> implementation.</param>
/// <param name="skillConversationIdFactoryBase">A <see cref="SkillConversationIdFactoryBase"/> implementation.</param>
/// <param name="languagePolicy">A <see cref="LanguagePolicy"/> to use.</param>
/// <param name="botFrameworkAuthentication">A <see cref="BotFrameworkAuthentication"/> used to obtain a client for making calls to Bot Builder Skills.</param>
/// <param name="telemetryClient">A <see cref="IBotTelemetryClient"/> used to log bot telemetry events.</param>
/// <param name="scopes">Custom <see cref="MemoryScope"/> implementations that extend the memory system.</param>
/// <param name="pathResolvers">Custom <see cref="IPathResolver"/> that add new resolvers path shortcuts to memory scopes.</param>
/// <param name="dialogs">Custom <see cref="Dialog"/> that will be added to the root DialogSet.</param>
/// <param name="logger">An <see cref="ILogger"/> instance.</param>
public AdaptiveDialogBot(
string adaptiveDialogId,
string languageGeneratorId,
ResourceExplorer resourceExplorer,
ConversationState conversationState,
UserState userState,
SkillConversationIdFactoryBase skillConversationIdFactoryBase,
LanguagePolicy languagePolicy,
BotFrameworkAuthentication botFrameworkAuthentication,
IBotTelemetryClient telemetryClient,
IEnumerable<MemoryScope> scopes = default,
IEnumerable<IPathResolver> pathResolvers = default,
IEnumerable<Dialog> dialogs = default,
ILogger logger = null)
: this(
adaptiveDialogId,
languageGeneratorId,
resourceExplorer,
conversationState,
userState,
skillConversationIdFactoryBase,
languagePolicy,
new LanguageGeneratorManager(resourceExplorer),
botFrameworkAuthentication,
telemetryClient,
scopes,
pathResolvers,
dialogs,
logger)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="AdaptiveDialogBot"/> class.
Expand All @@ -54,6 +103,7 @@ public class AdaptiveDialogBot : IBot
/// <param name="languagePolicy">A <see cref="LanguagePolicy"/> to use.</param>
/// <param name="botFrameworkAuthentication">A <see cref="BotFrameworkAuthentication"/> used to obtain a client for making calls to Bot Builder Skills.</param>
/// <param name="telemetryClient">A <see cref="IBotTelemetryClient"/> used to log bot telemetry events.</param>
/// <param name="languageGeneratorManager">A <see cref="LanguageGeneratorManager"/>.</param>
/// <param name="scopes">Custom <see cref="MemoryScope"/> implementations that extend the memory system.</param>
/// <param name="pathResolvers">Custom <see cref="IPathResolver"/> that add new resolvers path shortcuts to memory scopes.</param>
/// <param name="dialogs">Custom <see cref="Dialog"/> that will be added to the root DialogSet.</param>
Expand All @@ -66,6 +116,7 @@ public AdaptiveDialogBot(
UserState userState,
SkillConversationIdFactoryBase skillConversationIdFactoryBase,
LanguagePolicy languagePolicy,
LanguageGeneratorManager languageGeneratorManager,
BotFrameworkAuthentication botFrameworkAuthentication,
IBotTelemetryClient telemetryClient,
IEnumerable<MemoryScope> scopes = default,
Expand All @@ -89,7 +140,7 @@ public AdaptiveDialogBot(

_lazyRootDialog = new Lazy<Task<Dialog>>(CreateDialogAsync);
_lazyLanguageGenerator = new Lazy<LanguageGenerator>(CreateLanguageGenerator);
_lazyLanguageGeneratorManager = new Lazy<LanguageGeneratorManager>(CreateLanguageGeneratorManager);
_lazyLanguageGeneratorManager = languageGeneratorManager;
}

/// <inheritdoc/>
Expand All @@ -100,7 +151,9 @@ public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancel
using (var botFrameworkClient = _botFrameworkAuthentication.CreateBotFrameworkClient())
{
// Set up the TurnState the Dialog is expecting
await SetUpTurnStateAsync(turnContext, botFrameworkClient).ConfigureAwait(false);
SetupTurnState(turnContext, botFrameworkClient);

await SetupTestOptionsAsync(turnContext).ConfigureAwait(false);

// Load the Dialog from the ResourceExplorer - the actual load should only happen once
var rootDialog = await _lazyRootDialog.Value.ConfigureAwait(false);
Expand All @@ -114,7 +167,7 @@ public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancel
}
}

private async Task SetUpTurnStateAsync(ITurnContext turnContext, BotFrameworkClient botFrameworkClient)
private void SetupTurnState(ITurnContext turnContext, BotFrameworkClient botFrameworkClient)
{
turnContext.TurnState.Add(botFrameworkClient);
turnContext.TurnState.Add(_skillConversationIdFactoryBase);
Expand All @@ -124,16 +177,25 @@ private async Task SetUpTurnStateAsync(ITurnContext turnContext, BotFrameworkCli
turnContext.TurnState.Add(_memoryScopes);
turnContext.TurnState.Add(_pathResolvers);
turnContext.TurnState.Add(_lazyLanguageGenerator.Value);
turnContext.TurnState.Add(_lazyLanguageGeneratorManager.Value);
turnContext.TurnState.Add(_lazyLanguageGeneratorManager);
turnContext.TurnState.Add(_languagePolicy);
turnContext.TurnState.Add(_telemetryClient);

// Register the BotCallbackHandler in the TurnState using Set as opposed to Add
// because some adapters (like BotFrameworkAdapter and CloudAdapter) will have already added it
turnContext.TurnState.Set<BotCallbackHandler>(OnTurnAsync);
}

// TestOptions is enabled via Emulator for introducing randomization. Set when opening a bot in Emulator using
// the "Test Options - Random Seed" and "Test Options - Random Value".
private async Task SetupTestOptionsAsync(ITurnContext turnContext)
{
// Catch "SetTestOptions" event and save into "Conversation.TestOptions".
// Note: This is consumed by AdaptiveExpressions Extensions.RandomNext
if (turnContext.Activity.Type == ActivityTypes.Event)
{
var eventActivity = turnContext.Activity.AsEventActivity();
if (eventActivity.Name == "SetTestOptions")
if (eventActivity.Name == "SetTestOptions" && eventActivity.ChannelId == Channels.Emulator)
{
_logger.LogInformation("SetTestOptions received. This could change the behavior of AdaptiveExpression RandomNext.");

Expand All @@ -142,10 +204,6 @@ private async Task SetUpTurnStateAsync(ITurnContext turnContext, BotFrameworkCli
await property.SetAsync(turnContext, eventActivity.Value).ConfigureAwait(false);
}
}

// Register the BotCallbackHandler in the TurnState using Set as opposed to Add
// because some adapters (like BotFrameworkAdapter and CloudAdapter) will have already added it
turnContext.TurnState.Set<BotCallbackHandler>(OnTurnAsync);
}

private async Task<Dialog> CreateDialogAsync()
Expand Down Expand Up @@ -174,10 +232,5 @@ private LanguageGenerator CreateLanguageGenerator()
? (LanguageGenerator)new ResourceMultiLanguageGenerator(_languageGeneratorId)
: new TemplateEngineLanguageGenerator();
}

private LanguageGeneratorManager CreateLanguageGeneratorManager()
{
return new LanguageGeneratorManager(_resourceExplorer);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class LanguageGeneratorManager
/// <summary>
/// multi language lg resources. en -> [resourcelist].
/// </summary>
private readonly Dictionary<string, IList<Resource>> _multilanguageResources;
private readonly ConcurrentDictionary<string, IList<Resource>> _multilanguageResources;

/// <summary>
/// Initializes a new instance of the <see cref="LanguageGeneratorManager"/> class.
Expand All @@ -37,7 +37,7 @@ public class LanguageGeneratorManager
public LanguageGeneratorManager(ResourceExplorer resourceExplorer)
{
_resourceExplorer = resourceExplorer ?? throw new ArgumentNullException(nameof(resourceExplorer));
_multilanguageResources = LGResourceLoader.GroupByLocale(resourceExplorer);
_multilanguageResources = new ConcurrentDictionary<string, IList<Resource>>(LGResourceLoader.GroupByLocale(resourceExplorer));

PopulateLanguageGenerators();

Expand All @@ -59,7 +59,7 @@ public LanguageGeneratorManager(ResourceExplorer resourceExplorer)
/// <param name="locale">Locale to identify language.</param>
/// <param name="resourceMapping">Template resource loader delegate.</param>
/// <returns>The delegate to resolve the resource.</returns>
public static ImportResolverDelegate ResourceExplorerResolver(string locale, Dictionary<string, IList<Resource>> resourceMapping)
public static ImportResolverDelegate ResourceExplorerResolver(string locale, IDictionary<string, IList<Resource>> resourceMapping)
{
return (LGResource lgResource, string id) =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public TemplateEngineLanguageGenerator(LanguageGeneration.Templates engine = nul
/// <param name="id">optional label for the source of the templates (used for labeling source of template errors).</param>
/// <param name="resourceMapping">template resource loader delegate (locale) -> <see cref="ImportResolverDelegate"/>.</param>
[Obsolete("This method will soon be deprecated. Use LGResource as the first parameter instead.")]
public TemplateEngineLanguageGenerator(string lgText, string id, Dictionary<string, IList<Resource>> resourceMapping)
public TemplateEngineLanguageGenerator(string lgText, string id, IDictionary<string, IList<Resource>> resourceMapping)
{
Id = id ?? DEFAULTLABEL;
var (_, locale) = LGResourceLoader.ParseLGFileName(id);
Expand All @@ -67,7 +67,7 @@ public TemplateEngineLanguageGenerator(string lgText, string id, Dictionary<stri
/// <param name="filePath">lg template file absolute path.</param>
/// <param name="resourceMapping">template resource loader delegate (locale) -> <see cref="ImportResolverDelegate"/>.</param>
[Obsolete("This method will soon be deprecated. Use LGResource as the first parameter instead.")]
public TemplateEngineLanguageGenerator(string filePath, Dictionary<string, IList<Resource>> resourceMapping)
public TemplateEngineLanguageGenerator(string filePath, IDictionary<string, IList<Resource>> resourceMapping)
{
filePath = PathUtils.NormalizePath(filePath);
Id = Path.GetFileName(filePath);
Expand All @@ -83,7 +83,7 @@ public TemplateEngineLanguageGenerator(string filePath, Dictionary<string, IList
/// </summary>
/// <param name="resource">Resource.</param>
/// <param name="resourceMapping">template resource loader delegate (locale) -> <see cref="ImportResolverDelegate"/>.</param>
public TemplateEngineLanguageGenerator(Resource resource, Dictionary<string, IList<Resource>> resourceMapping)
public TemplateEngineLanguageGenerator(Resource resource, IDictionary<string, IList<Resource>> resourceMapping)
{
Id = resource.Id;
_lg = new Lazy<Task<LanguageGeneration.Templates>>(() => CreateTemplatesAsync(resource, resourceMapping));
Expand Down Expand Up @@ -171,7 +171,7 @@ private static void RegisterSourcemap(object item, LanguageGeneration.SourceRang
/// <param name="resource">Resource.</param>
/// <param name="resourceMapping">template resource loader delegate (locale) -> <see cref="ImportResolverDelegate"/>.</param>
/// <returns>The loaded language generation templates.</returns>
private async Task<LanguageGeneration.Templates> CreateTemplatesAsync(Resource resource, Dictionary<string, IList<Resource>> resourceMapping)
private async Task<LanguageGeneration.Templates> CreateTemplatesAsync(Resource resource, IDictionary<string, IList<Resource>> resourceMapping)
{
var (_, locale) = LGResourceLoader.ParseLGFileName(Id);
var importResolver = LanguageGeneratorManager.ResourceExplorerResolver(locale, resourceMapping);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,6 @@
<PackageReference Include="Microsoft.AspNet.WebApi">
<Version>5.2.6</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNet.WebApi.Owin">
<Version>5.2.6</Version>
</PackageReference>
<PackageReference Include="Microsoft.CodeCoverage">
<Version>16.3.0</Version>
</PackageReference>
Expand Down