AttributeScorable.cs
1 //
2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license.
4 //
5 // Microsoft Bot Framework: http://botframework.com
6 //
7 // Bot Builder SDK Github:
8 // https://github.com/Microsoft/BotBuilder
9 //
10 // Copyright (c) Microsoft Corporation
11 // All rights reserved.
12 //
13 // MIT License:
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33 
38 using System;
39 using System.Collections.Generic;
40 using System.Linq;
41 using System.Reflection;
42 using System.Text;
43 using System.Text.RegularExpressions;
44 using System.Threading;
45 using System.Threading.Tasks;
46 
47 namespace Microsoft.Bot.Builder.Internals.Scorables
48 {
52  public sealed class AttributeScorable : DelegatingScorable<IResolver, object>
53  {
54  public AttributeScorable(Type type, Func<ILuisModel, ILuisService> make)
55  : base(Make(type, make))
56  {
57  }
58  public static IEnumerable<A> AttributesFor<A>(MethodInfo method) where A : Attribute
59  {
60  var m = method.GetCustomAttributes<A>(inherit: true);
61  var t = method.DeclaringType.GetCustomAttributes<A>(inherit: true);
62 
63  return m.Concat(t);
64  }
65 
66  public static IScorable<IResolver, object> Make(Type type, Func<ILuisModel, ILuisService> make)
67  {
68  var methods = type.GetMethods();
69  var scorableByMethod = methods.ToDictionary(m => m, m => new MethodScorable(m));
70 
71  var luisSpec =
72  from method in methods
73  from model in AttributesFor<LuisModelAttribute>(method)
74  from intent in AttributesFor<LuisIntentAttribute>(method)
75  select new { method, intent, model };
76 
77  // for a given LUIS model and intent, fold the corresponding method scorables together to enable overload resolution
78  var luisScorables =
79  from spec in luisSpec
80  group spec by new { spec.model, spec.intent } into modelIntents
81  let method = modelIntents.Select(m => scorableByMethod[m.method]).ToArray().Fold(Binding.ResolutionComparer.Instance)
82  select new LuisIntentScorable<Binding, Binding>(modelIntents.Key.model, modelIntents.Key.intent, method);
83 
84  var regexSpec =
85  from method in methods
86  from pattern in AttributesFor<RegexPatternAttribute>(method)
87  select new { method, pattern };
88 
89  // for a given regular expression pattern, fold the corresponding method scorables together to enable overload resolution
90  var regexScorables =
91  from spec in regexSpec
92  group spec by spec.pattern into patterns
93  let method = patterns.Select(m => scorableByMethod[m.method]).ToArray().Fold(Binding.ResolutionComparer.Instance)
94  let regex = new Regex(patterns.Key.Pattern)
95  select new RegexMatchScorable<Binding, Binding>(regex, method);
96 
97  var methodScorables = scorableByMethod.Values;
98 
99  // fold all of the scorables together by score type, and select the best
100  IScorable<IResolver, object> regexAll = regexScorables.ToArray().Fold(MatchComparer.Instance);//.Select((_, m) => 1.0);
101  IScorable<IResolver, object> luisAll = luisScorables.ToArray().Fold(IntentComparer.Instance);//.Select((_, i) => i.Score.Value);
102  IScorable<IResolver, object> methodAll = methodScorables.ToArray().Fold(Binding.ResolutionComparer.Instance);//.Select((_, b) => 1.0);
103 
104  // introduce a precedence order to prefer regex over luis over generic method matches
105  var all = new[] { regexAll, luisAll, methodAll };
106 
107  // select the first scorable that matches
108  var winner = all.First();
109 
110  // introduce a lifetime scope to the scoring process to memoize service calls to LUIS
111  TryResolve tryResolve = (Type t, object tag, out object value) =>
112  {
113  var model = tag as ILuisModel;
114  if (model != null)
115  {
116  value = new MemoizingLuisService(make(model));
117  return true;
118  }
119 
120  value = null;
121  return false;
122  };
123  var scope = new ScopeScorable<object, object>(tryResolve, winner);
124  return scope;
125  }
126 
127  private sealed class MemoizingLuisService : ILuisService
128  {
129  private readonly ILuisService service;
130  private Uri uri;
131  private LuisResult result;
132  public MemoizingLuisService(ILuisService service)
133  {
134  SetField.NotNull(out this.service, nameof(service), service);
135  }
136 
137  Uri ILuisService.BuildUri(string text)
138  {
139  return this.service.BuildUri(text);
140  }
141 
142  async Task<LuisResult> ILuisService.QueryAsync(Uri uri, CancellationToken token)
143  {
144  if (result == null)
145  {
146  this.uri = uri;
147  this.result = await this.service.QueryAsync(uri, token);
148  }
149 
150  if (this.uri != uri)
151  {
152  throw new InvalidOperationException();
153  }
154 
155  return this.result;
156  }
157  }
158  }
159 }
A mockable interface for the LUIS service.
Definition: LuisService.cs:50
Represents a binding of arguments to a method&#39;s parameters.
Definition: Binding.cs:49
Task< LuisResult > QueryAsync(Uri uri, CancellationToken token)
Query the LUIS service using this uri.
static IScorable< IResolver, object > Make(Type type, Func< ILuisModel, ILuisService > make)
Namespace for internal machinery that is not useful for most developers.
Scorable to represent a regular expression match against an activity&#39;s text.
delegate bool TryResolve(Type type, object tag, out object value)
Scorable to represent binding arguments to a method&#39;s parameters.
AttributeScorable(Type type, Func< ILuisModel, ILuisService > make)
A mockable interface for the LUIS model.
Definition: LuisModel.cs:46
Scorable to represent a specific LUIS intent recommendation.
Namespace for the internal fibers machinery that is not useful for most developers.
Definition: Awaitable.cs:36
Core namespace for Dialogs and associated infrastructure.
Definition: Address.cs:40
Allow the scoring of items, with external comparison of scores, and enable the winner to take some ac...
Definition: Scorable.cs:50
Namespace for the machinery needed to talk to http://luis.ai.
Scorable for introducing a lifetime scope to resources needed during a IScorable.PrepareAsync.
Create a dispatch scorable based on attributes.
static readonly IComparer< IntentRecommendation > Instance
Root namespace for the Microsoft Bot Builder SDK.
Uri BuildUri(string text)
Build the query uri for the query text.