LuisService.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 
34 using System;
35 using System.Collections.Generic;
36 using System.Net.Http;
37 using System.Threading;
38 using System.Threading.Tasks;
41 using Microsoft.Rest;
42 using Newtonsoft.Json;
43 
44 namespace Microsoft.Bot.Builder.Luis
45 {
49  public sealed class LuisRequest
50  {
54  public readonly string Query;
55 
59  public readonly double? TimezoneOffset;
60 
64  public readonly string ContextId;
65 
69  public readonly bool? Verbose;
70 
74  public readonly string ForceSet;
75 
79  public readonly string AllowSampling;
80 
90  public LuisRequest(string query, double? timezoneOffset = default(double?),
91  string contextId = default(string), bool? verbose = default(bool?), string forceSet = default(string),
92  string allowSampling = default(string))
93  {
94  this.Query = query;
95  this.TimezoneOffset = timezoneOffset;
96  this.ContextId = contextId;
97  this.Verbose = verbose;
98  this.ForceSet = forceSet;
99  this.AllowSampling = allowSampling;
100  }
101 
107  public Uri BuildUri(ILuisModel model)
108  {
109  if (model.ModelID == null)
110  {
111  throw new ValidationException(ValidationRules.CannotBeNull, "id");
112  }
113  if (model.SubscriptionKey == null)
114  {
115  throw new ValidationException(ValidationRules.CannotBeNull, "subscriptionKey");
116  }
117 
118  var queryParameters = new List<string>();
119  queryParameters.Add($"subscription-key={Uri.EscapeDataString(model.SubscriptionKey)}");
120  queryParameters.Add($"q={Uri.EscapeDataString(Query)}");
121  UriBuilder builder;
122 
123  var id = Uri.EscapeDataString(model.ModelID);
124  switch (model.ApiVersion)
125  {
126 #pragma warning disable CS0612
127  case LuisApiVersion.V1:
128  builder = new UriBuilder(model.UriBase);
129  queryParameters.Add($"id={id}");
130  break;
131 #pragma warning restore CS0612
132  case LuisApiVersion.V2:
133  //v2.0 have the model as path parameter
134  builder = new UriBuilder(new Uri(model.UriBase, id));
135  break;
136  default:
137  throw new ArgumentException($"{model.ApiVersion} is not a valid Luis api version.");
138  }
139 
140  if (TimezoneOffset != null)
141  {
142  queryParameters.Add($"timezoneOffset={Uri.EscapeDataString(Convert.ToString(TimezoneOffset))}");
143  }
144  if (ContextId != null)
145  {
146  queryParameters.Add($"contextId={Uri.EscapeDataString(ContextId)}");
147  }
148  if (Verbose != null)
149  {
150  queryParameters.Add($"verbose={Uri.EscapeDataString(Convert.ToString(Verbose))}");
151  }
152  if (ForceSet != null)
153  {
154  queryParameters.Add($"forceSet={Uri.EscapeDataString(ForceSet)}");
155  }
156  if (AllowSampling != null)
157  {
158  queryParameters.Add($"allowSampling={Uri.EscapeDataString(AllowSampling)}");
159  }
160 
161  builder.Query = string.Join("&", queryParameters);
162  return builder.Uri;
163  }
164  }
165 
169  public interface ILuisService
170  {
176  Uri BuildUri(LuisRequest luisRequest);
177 
184  Task<LuisResult> QueryAsync(Uri uri, CancellationToken token);
185  }
186 
190  [Serializable]
191  public sealed class LuisService : ILuisService
192  {
193  private readonly ILuisModel model;
194 
199  public LuisService(ILuisModel model)
200  {
201  SetField.NotNull(out this.model, nameof(model), model);
202  }
203 
204  Uri ILuisService.BuildUri(LuisRequest luisRequest)
205  {
206  return luisRequest.BuildUri(this.model);
207  }
208 
209  async Task<LuisResult> ILuisService.QueryAsync(Uri uri, CancellationToken token)
210  {
211  string json;
212  using (var client = new HttpClient())
213  using (var response = await client.GetAsync(uri, HttpCompletionOption.ResponseContentRead, token))
214  {
215  response.EnsureSuccessStatusCode();
216  json = await response.Content.ReadAsStringAsync();
217  }
218 
219  try
220  {
221  var result = JsonConvert.DeserializeObject<LuisResult>(json);
222  // fix up Luis result for backward compatibility
223  // v2 api is not returning list of intents if verbose query parameter
224  // is not set. This will move IntentRecommendation in TopScoringIntent
225  // to list of Intents.
226  if (result.TopScoringIntent != null && result.Intents == null)
227  {
228  result.Intents = new List<IntentRecommendation> { result.TopScoringIntent };
229  }
230  return result;
231  }
232  catch (JsonException ex)
233  {
234  throw new ArgumentException("Unable to deserialize the LUIS response.", ex);
235  }
236  }
237  }
238 
242  public static partial class Extensions
243  {
251  public static async Task<LuisResult> QueryAsync(this ILuisService service, string text, CancellationToken token)
252  {
253  var uri = service.BuildUri(new LuisRequest(query: text));
254  return await service.QueryAsync(uri, token);
255  }
256 
263  public static Uri BuildUri(this ILuisService service, string text)
264  {
265  return service.BuildUri(new LuisRequest(query: text));
266  }
267  }
268 }
269 
A mockable interface for the LUIS service.
Definition: LuisService.cs:169
readonly string Query
The text query.
Definition: LuisService.cs:54
readonly string AllowSampling
Indicates if sampling is allowed.
Definition: LuisService.cs:79
Task< LuisResult > QueryAsync(Uri uri, CancellationToken token)
Query the LUIS service using this uri.
static Uri BuildUri(this ILuisService service, string text)
Builds luis uri with text query.
Definition: LuisService.cs:263
Namespace for models generated from the http://luis.ai REST API.
Definition: FormDialog.cs:837
static async Task< LuisResult > QueryAsync(this ILuisService service, string text, CancellationToken token)
Query the LUIS service using this text.
Definition: LuisService.cs:251
LuisApiVersion
Luis api version.
Definition: LuisModel.cs:43
Uri BuildUri(LuisRequest luisRequest)
Build the query uri for the LuisRequest.
Uri UriBase
The base Uri for accessing LUIS.
Definition: LuisModel.cs:68
readonly string ForceSet
Force setting the parameter.
Definition: LuisService.cs:74
string SubscriptionKey
The LUIS subscription key.
Definition: LuisModel.cs:63
Namespace for internal machinery that is not useful for most developers and may change in the future...
readonly double TimezoneOffset
The time zone offset.
Definition: LuisService.cs:59
LuisService(ILuisModel model)
Construct the LUIS service using the model information.
Definition: LuisService.cs:199
LuisApiVersion ApiVersion
Luis Api Version.
Definition: LuisModel.cs:73
Uri BuildUri(ILuisModel model)
Build the Uri for issuing the request for the specified Luis model.
Definition: LuisService.cs:107
Standard implementation of ILuisService against actual LUIS service.
Definition: LuisService.cs:191
A mockable interface for the LUIS model.
Definition: LuisModel.cs:53
LuisRequest(string query, double?timezoneOffset=default(double?), string contextId=default(string), bool?verbose=default(bool?), string forceSet=default(string), string allowSampling=default(string))
Constructs an instance of the LuisReqeuest.
Definition: LuisService.cs:90
readonly bool Verbose
The verbose flag.
Definition: LuisService.cs:69
IList< IntentRecommendation > Intents
The intents found in the query text.
Definition: LuisResult.cs:54
Namespace for the internal fibers machinery that is not useful for most developers and may change in ...
Namespace for the machinery needed to talk to http://luis.ai.
readonly string ContextId
The context id.
Definition: LuisService.cs:64
Object that contains all the possible parameters to build Luis request.
Definition: LuisService.cs:49
string ModelID
The LUIS model ID.
Definition: LuisModel.cs:58
Root namespace for the Microsoft Bot Builder SDK.