[LG] Add CacheScope option (#4602)
* add cache option * app ! to global * apply reexecute * add more test and renaming * revert the schema file * fix typo * rename template -> locale
This commit is contained in:
Родитель
a69996b46f
Коммит
dff5dc6611
|
@ -24,6 +24,27 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
Markdown,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LG cache scope options.
|
||||
/// </summary>
|
||||
public enum LGCacheScope
|
||||
{
|
||||
/// <summary>
|
||||
/// Global template cache scope.
|
||||
/// </summary>
|
||||
Global,
|
||||
|
||||
/// <summary>
|
||||
/// Only cache result in the same layer of children in template.
|
||||
/// </summary>
|
||||
Local,
|
||||
|
||||
/// <summary>
|
||||
/// Without cache.
|
||||
/// </summary>
|
||||
None
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Options for evaluating LG templates.
|
||||
/// </summary>
|
||||
|
@ -43,6 +64,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
NullSubstitution = null;
|
||||
LineBreakStyle = null;
|
||||
Locale = null;
|
||||
CacheScope = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -55,6 +77,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
NullSubstitution = opt.NullSubstitution;
|
||||
LineBreakStyle = opt.LineBreakStyle;
|
||||
Locale = opt.Locale ?? Thread.CurrentThread.CurrentCulture.Name;
|
||||
CacheScope = opt.CacheScope;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -125,6 +148,14 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// </value>
|
||||
public Func<string, object> NullSubstitution { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets cache scope of the evaluation result.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Cache scope of the evaluation result.
|
||||
/// </value>
|
||||
public LGCacheScope? CacheScope { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Merge a incoming option to current option. If a property in incoming option is not null while it is null in current
|
||||
/// option, then the value of this property will be overwritten.
|
||||
|
|
|
@ -23,12 +23,12 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the children template that this template has evaluated currently.
|
||||
/// Gets or sets the children template that this template has evaluated and cached currently.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The children template that this template has evaluated currently.
|
||||
/// </value>
|
||||
public Dictionary<string, object> EvaluatedChildren { get; set; } = new Dictionary<string, object>();
|
||||
public Dictionary<string, object> CachedEvaluatedChildren { get; set; } = new Dictionary<string, object>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets template name.
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
@ -23,6 +24,8 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
internal const string LGType = "lgType";
|
||||
|
||||
private const string ReExecuteSuffix = "!";
|
||||
|
||||
private static readonly ConcurrentDictionary<string, object> _cachedResult = new ConcurrentDictionary<string, object>();
|
||||
private readonly Stack<EvaluationTarget> _evaluationTargetStack = new Stack<EvaluationTarget>();
|
||||
private readonly EvaluationOptions _lgOptions;
|
||||
|
||||
|
@ -85,7 +88,6 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
}
|
||||
|
||||
var templateTarget = new EvaluationTarget(templateName, memory);
|
||||
|
||||
var currentEvaluateId = templateTarget.GetId();
|
||||
|
||||
if (_evaluationTargetStack.Any(e => e.GetId() == currentEvaluateId))
|
||||
|
@ -93,26 +95,55 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
throw new Exception($"{TemplateErrors.LoopDetected} {string.Join(" => ", _evaluationTargetStack.Reverse().Select(e => e.TemplateName))} => {templateName}");
|
||||
}
|
||||
|
||||
EvaluationTarget previousEvaluateTarget = null;
|
||||
if (_evaluationTargetStack.Count != 0)
|
||||
object result = null;
|
||||
var hasResult = false;
|
||||
if (!reExecute)
|
||||
{
|
||||
previousEvaluateTarget = _evaluationTargetStack.Peek();
|
||||
|
||||
if (!reExecute && previousEvaluateTarget.EvaluatedChildren.ContainsKey(currentEvaluateId))
|
||||
if (_lgOptions.CacheScope == LGCacheScope.Global)
|
||||
{
|
||||
return previousEvaluateTarget.EvaluatedChildren[currentEvaluateId];
|
||||
if (_cachedResult.ContainsKey(currentEvaluateId))
|
||||
{
|
||||
result = _cachedResult[currentEvaluateId];
|
||||
hasResult = true;
|
||||
}
|
||||
}
|
||||
else if (_lgOptions.CacheScope == null || _lgOptions.CacheScope == LGCacheScope.Local)
|
||||
{
|
||||
EvaluationTarget previousEvaluateTarget = null;
|
||||
if (_evaluationTargetStack.Count != 0)
|
||||
{
|
||||
previousEvaluateTarget = _evaluationTargetStack.Peek();
|
||||
|
||||
if (previousEvaluateTarget.CachedEvaluatedChildren.ContainsKey(currentEvaluateId))
|
||||
{
|
||||
result = previousEvaluateTarget.CachedEvaluatedChildren[currentEvaluateId];
|
||||
hasResult = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Using a stack to track the evaluation trace
|
||||
_evaluationTargetStack.Push(templateTarget);
|
||||
var result = Visit(TemplateMap[templateName].TemplateBodyParseTree);
|
||||
if (previousEvaluateTarget != null)
|
||||
if (!hasResult)
|
||||
{
|
||||
previousEvaluateTarget.EvaluatedChildren[currentEvaluateId] = result;
|
||||
}
|
||||
_evaluationTargetStack.Push(templateTarget);
|
||||
result = Visit(TemplateMap[templateName].TemplateBodyParseTree);
|
||||
_evaluationTargetStack.Pop();
|
||||
|
||||
_evaluationTargetStack.Pop();
|
||||
if (!reExecute)
|
||||
{
|
||||
if (_lgOptions.CacheScope == LGCacheScope.Global)
|
||||
{
|
||||
_cachedResult[currentEvaluateId] = result;
|
||||
}
|
||||
else if (_lgOptions.CacheScope == null || _lgOptions.CacheScope == LGCacheScope.Local)
|
||||
{
|
||||
if (_evaluationTargetStack.Count > 0)
|
||||
{
|
||||
_evaluationTargetStack.Peek().CachedEvaluatedChildren[currentEvaluateId] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# templateWithSameParams(param)
|
||||
- ${listTemplate1(param)} ${listTemplate1!(param)}
|
||||
- ${listTemplate1(param)} ${listTemplate1!(param)} ${listTemplate1(param)}
|
||||
|
||||
# listTemplate1(param)
|
||||
- item1
|
||||
|
|
|
@ -9,4 +9,11 @@
|
|||
- item2
|
||||
- item3
|
||||
- item4
|
||||
- item5
|
||||
- item5
|
||||
|
||||
|
||||
# globalCache(param)
|
||||
- ${subTemplate(param)} ${listTemplate1(param)}
|
||||
|
||||
# subTemplate(param)
|
||||
- ${listTemplate1(param)}
|
|
@ -51,13 +51,13 @@
|
|||
<None Remove="Examples\NonAdaptiveCardActivity.lg" />
|
||||
<None Remove="Examples\StrictModeFalse.lg" />
|
||||
<None Remove="Examples\switchcase.lg" />
|
||||
<None Remove="Examples\TemplateCache.lg" />
|
||||
<None Remove="Examples\TemplateNameWithDot.lg" />
|
||||
<None Remove="Examples\TemplateRef.lg" />
|
||||
<None Remove="Examples\Regex.lg" />
|
||||
<None Remove="Examples\EvalExpression.lg" />
|
||||
<None Remove="Examples\StructuredTemplate.lg" />
|
||||
<None Remove="Examples\ConditionExpression.lg" />
|
||||
<None Remove="Examples\EvaluateOnce.lg" />
|
||||
<None Remove="Examples\LoopScope.lg" />
|
||||
<None Remove="Examples\ExpressionExtract.lg" />
|
||||
<None Remove="Examples\StringInterpolation.lg" />
|
||||
|
@ -233,7 +233,7 @@
|
|||
<Content Include="Examples\StructuredTemplate.lg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Examples\EvaluateOnce.lg">
|
||||
<Content Include="Examples\TemplateCache.lg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Examples\ReExecute.lg">
|
||||
|
|
|
@ -1107,9 +1107,9 @@ namespace Microsoft.Bot.Builder.AI.LanguageGeneration.Tests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void TestEvaluateOnce()
|
||||
public void TestTemplateCache()
|
||||
{
|
||||
var templates = Templates.ParseFile(GetExampleFilePath("EvaluateOnce.lg"));
|
||||
var templates = Templates.ParseFile(GetExampleFilePath("TemplateCache.lg"));
|
||||
|
||||
var evaled = templates.Evaluate("templateWithSameParams", new { param = "ms" });
|
||||
Assert.NotNull(evaled);
|
||||
|
@ -1120,6 +1120,13 @@ namespace Microsoft.Bot.Builder.AI.LanguageGeneration.Tests
|
|||
|
||||
// may be has different values
|
||||
evaled = templates.Evaluate("templateWithDifferentParams", new { param1 = "ms", param2 = "newms" });
|
||||
|
||||
// global cache test
|
||||
evaled = templates.Evaluate("globalCache", new { param = "ms" }, new EvaluationOptions { CacheScope = LGCacheScope.Global });
|
||||
|
||||
resultList = evaled.ToString().Split(" ");
|
||||
Assert.True(resultList.Length == 2);
|
||||
Assert.True(resultList[0] == resultList[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -1129,6 +1136,11 @@ namespace Microsoft.Bot.Builder.AI.LanguageGeneration.Tests
|
|||
|
||||
// may be has different values
|
||||
var evaled = templates.Evaluate("templateWithSameParams", new { param1 = "ms", param2 = "newms" });
|
||||
|
||||
// the third one should be the same with the first one
|
||||
var resultList = evaled.ToString().Split(" ");
|
||||
Assert.True(resultList.Length == 3);
|
||||
Assert.True(resultList[0] == resultList[2]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
Загрузка…
Ссылка в новой задаче