LuisV3.PredictionOptions doesn't let me timezone - how to do it? - c#

I figured I'd upgrade my LuisRecognizer to use LuisRecognizerOptionsV3. However I can't seem to set prediction options the way I like - how do I set the timezone? The v3 prediction options lack this field.
In my bot I am currently doing:
var predictionOptions = new LuisPredictionOptions();
predictionOptions.TimezoneOffset = turnContext.Activity.LocalTimestamp.Value.Offset.TotalMinutes;
and I can't figure out the equivalent in v3's version of the data structure.

The timezoneOffset parameter was mostly provided as a way to determine what day it is for the user in case they say something like "today" or "tomorrow." It also helps when the user enters a relative time like "in three hours." When using the timezoneOffset parameter, the returned entity is in the provided timezone rather than universal time.
In LUIS v3, instead of providing an offset you provide a DateTime reference and LUIS uses that to process relative time. You can see that documented here. Note that the datetimeReference property is only available in POST requests and not GET requests because you provide it in the request body and not as a query parameter.
Also note that the datetimeReference property is not currently available in the Bot Builder SDK. You can write your own code to access the LUIS API directly with an HttpClient, but if you'd still like a prebuilt SDK to handle things then you can use this NuGet package: Microsoft.Azure.CognitiveServices.Language.LUIS.Runtime 3.0.0
Here's an example of how to use it:
var appId = new Guid("<LUIS APP ID>");
var client = new LUISRuntimeClient(new ApiKeyServiceClientCredentials("<SERVICE KEY>"));
client.Endpoint = "https://westus2.api.cognitive.microsoft.com";
var options = new PredictionRequestOptions(activity.LocalTimestamp.Value.DateTime);
var request = new PredictionRequest("Book a flight in three hours", options);
var response = await client.Prediction.GetSlotPredictionAsync(appId, "PRODUCTION", request);
Console.WriteLine(JsonConvert.SerializeObject(response.Prediction.Entities, Formatting.Indented));

Related

Convert a Kerberos based WindowsIdentity into a Base64 string

If I have the following code:
var token = System.Security.Principal.WindowsIdentity.GetCurrent();
I get a WindowsIdentity that is Kerberos based. I need to make a call with a header like this:
Authorize: Negotiate <Kerberos Token Here>
Is there a way to convert that token object into a Base64 string?
As the maintainer of the Kerberos.NET as mentioned in the other answer, you can always go that route. However, if you want SSO using the currently signed in Windows identity you have to go through Windows' SSPI stack.
Many .NET clients already support this natively using Windows Integrated auth, its just a matter of finding the correct knobs. It's unclear what client you're using so I can't offer any suggestions beyond that.
However if this is a custom client you have to call into SSPI directly. There's a handful of really good answers for explaining how to do that such as: Client-server authentication - using SSPI?.
The aforementioned Kerberos.NET library does have a small wrapper around SSPI: https://github.com/dotnet/Kerberos.NET/blob/develop/Kerberos.NET/Win32/SspiContext.cs
It's pretty trivial:
using (var context = new SspiContext($"host/yourremoteserver.com", "Negotiate"))
{
var tokenBytes = context.RequestToken();
var header = "Negotiate " + Convert.ToBase64String(tokenBytes);
...
}
I could not get this to work, but I was able to get a token using the excellent Kerberos.NET NuGet package. With that I was able to get it like this:
var client = new KerberosClient();
var kerbCred = new KerberosPasswordCredential("UserName", "p#ssword", "domain");
await client.Authenticate(kerbCred);
Console.WriteLine(client.UserPrincipalName);
var ticket = await client.GetServiceTicket("http/ServerThatWantsTheKerberosTicket.domain.net");
return Convert.ToBase64String(ticket.EncodeGssApi().ToArray());
As an aside, I needed help figuring out what the SPN value was for the GetServiceTicket and the project maintainer was fantastically helpful (and fast!).

Azure Custom Vision API returning different results than project portal?

I've create a custom vision project to recognise characters (A, B, C...).
What is interesting: if I upload an image of a character (in this case an "N") to the vision API portal it will tell me that it is 99.9% sure it is an "N":
If however I use the client libraries to predict the very same image, I'm getting 53% that it is a "W" and only 37% that it is an "N":
I double checked that the latest iteration is the published one
I double checked that I'm using the correct project ID
My endpoint is set to "https://westeurope.api.cognitive.microsoft.com" in the CustomVisionPredictionClient
The code to get the prediction on my client:
var client = new CustomVisionPredictionClient()
{
ApiKey = predictionKey,
Endpoint = endpoint
};
var result = await client.PredictImageAsync(Guid.Parse(projectId), imageStream).ConfigureAwait(false);
var prediction = result.Predictions.FirstOrDefault();
Where does this difference come from and how to fix because according to the tests I did by uploading images the results are close to 100% correct no matter which character image I upload?
UPDATE: I noticed that there was an update for the client libraries. They went from 0.12pre to 1.0stable. After the update the PredictImageAsync is gone and replaced with DetectImageAsync. This expected as an additional parameter a model name. I tried using the name of the iteration and after a while the method returns with an internal server error. So not sure what to try next.
The comment above pointed my into the right direction - thanks!
The new client library has got two methods ClassifyImage and DetectImage (and various variations of them) which replace the previously used ones including PredictImage which I was using with the preview version of the client library.
To classify an image (which is what I wanted to do) ClassifyImage should of course be used. The new code looks like this and delivers an almost 100% correct prediction:
var client = new CustomVisionPredictionClient()
{
ApiKey = predictionKey,
Endpoint = endpoint
};
var result = await client.ClassifyImageAsync(Guid.Parse(projectId), "Iteration12", imageStream).ConfigureAwait(false);
var prediction = result.Predictions.FirstOrDefault();
endpoint is the URL of the region the vision API is hosted in, in my case https://westeurope.api.cognitive.microsoft.com.
predictionKey is available on the CustomVision.AI site in your project, so is the projectId
The publishedName parameter is the name of the iteration to use (in my case "Iteration12"

Microsoft Cognitive Services - Limit Intents in Luis Api

We have started experimenting with the Microsoft.CognitiveServices.Speech nuget package (https://learn.microsoft.com/en-gb/azure/cognitive-services/speech-service/how-to-recognize-intents-from-speech-csharp). This is great as it allows a language model to be built up and you can specifically include the intents you want to be matched:
// Creates a Language Understanding model using the app id, and adds specific intents from your model
var model = LanguageUnderstandingModel.FromAppId("YourLanguageUnderstandingAppId");
recognizer.AddIntent(model, "YourLanguageUnderstandingIntentName1", "id1");
recognizer.AddIntent(model, "YourLanguageUnderstandingIntentName2", "id2");
recognizer.AddIntent(model, "YourLanguageUnderstandingIntentName3", "any-IntentId-here");
However, we are building an API and will be passing text over and this will call Luis using the API endpoint, this is very basic like so:
using (var client = new HttpClient())
{
var queryString = HttpUtility.ParseQueryString(String.Empty);
// The request header contains your subscription key
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", _cognitiveSettings.SubscriptionKey);
// The "q" parameter contains the utterance to send to LUIS
queryString["q"] = query;
// These optional request parameters are set to their default values
queryString["staging"] = _cognitiveSettings.IsProduction ? bool.FalseString : bool.TrueString;
queryString["timezoneOffset"] = "0";
queryString["verbose"] = "true";
queryString["spellCheck"] = "false";
var endpointUri = $"https://westeurope.api.cognitive.microsoft.com/luis/v2.0/apps/{_luisAppId}?{queryString}";
var response = await client.GetAsync(endpointUri);
var responseJson = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<IntentResponseModel>(responseJson);
}
Is there a way to control the intents we would want to be returned for a particular text string? We can set verbose to true so it returns all intents with a match rating but we would prefer to be able to specify a subset of intents depending on the state and just try to match those. It seems you can do this with the SDK using audio, can this be done using text (is there a text SDK?).
Also, is there a way for the entitites that are returned in the JSON to be matched to the intent which populated them, it looks like there is no link between the intents and the entities.
Unfortunately, no, there is no way to control the intents that are returned. In the app, you will need to filter on the intents returned to limit what is matched for a particular text string.

Clarification on how to update (patch) objects using the Microsoft.Graph Client

The following code is the only way I found so far to update an object using the Microsoft Graph Client Library
Scenario:
Load an exisiting object (an organization)
Modify a value (add entry in securityComplianceNotificationPhones)
Send the update
Code
var client = new GraphServiceClient(...);
var org = client.Organization["orgid"].Request().GetAsync().Result;
var secPhones = new List<string>(org.SecurityComplianceNotificationPhones);
secPhones.Add("12345");
var patchOrg = new Organization();
patchOrg.SecurityComplianceNotificationPhones = secPhones;
var orgReq = new OrganizationRequest(
client.Organization[org.Id].Request().RequestUrl,
client, new Option[] {});
orgReq.UpdateAsync(patchOrg).Wait();
I needed to use the patchOrg instance because of two things:
The Graph API documentation states
"In the request body, supply the values for relevant fields that
should be updated. Existing properties that are not included in the
request body will maintain their previous values or be recalculated
based on changes to other property values. For best performance you
shouldn't include existing values that haven't changed."
If you actually do include existing values that haven't changed
(i.e. assginedLicenses) the request fails, if those existing values
are readonly.
My question is: Is/will there be a more straightforward way of updating existing objects like for example in the Azure ActiveDirectory GraphClient? Just for comparison, the same scenario in Azure Active Directory Graph
var client = new ActiveDirectoryClient(...);
var org = client.TenantDetails.GetByObjectId("orgid").ExecuteAsync().Result;
org.SecurityComplianceNotificationPhones.Add("12345");
org.UpdateAsync().Wait();
The Graph client library model is slightly different from the older SDK model the AAD client library you linked. The older model passed around objects that tried to be a bit smarter and reason about which properties were changed, only sending those. One of the main drawbacks of this model was that the library made many more service calls in the background and had a much heavier payload in each call since ExecuteAsync() would often need to retrieve every object in the request builder chain. The newer library does require the developer to do more explicit reasoning about what data is being passed but also gives greater control over network calls and payload. Each model has its tradeoffs.
To accomplish what you want, here's the approach I would recommend instead of creating a second org object altogether:
var client = new GraphServiceClient(...);
var orgRequest = client.Organization["orgid"].Request();
var org = orgRequest.Select("securityComplianceNotificationPhones").GetAsync().Result;
var secPhones = new List<string>(org.SecurityComplianceNotificationPhones);
secPhones.Add("12345");
org.SecurityComplianceNotificationPhones = secPhones;
orgRequest.UpdateAsync(org).Wait();

Skybrud social Log in via Facebook?

I'm usinng skybrud social to allow users to log into my site via Facebook, but am having a problem.
For some reason, the response never contains anything other than the Name and Id of the user... everything else is null.
var url = client.GetAuthorizationUrl(state, "public_profile", "email");
var service = FacebookService.CreateFromAccessToken(userAccessToken);
FacebookMeResponse user = service.Methods.Me();
Has anyone experienced this before? What could be the problem?
Facebook has multiple versions of their Graph API. In the most recent version (2.4), less fields are returned by default, and you instead have to tell the API to return the fields that you need. What version of the API you're using depends on the time you registered your app with Facebook.
Based on your code, it seems that you're using an older version of Skybrud.Social. If you update to the most recent version (0.9.4.1), you can do something like this:
// Declare the options for the call to the API
FacebookGetUserOptions options = new FacebookGetUserOptions("me") {
Fields = "name,email,gender"
};
// Make the call to the API
FacebookUserResponse response = service.Users.GetUser(options);
Hope this answers your questions ;)

Categories