Skip to content
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

Client configuration #4

Merged
merged 23 commits into from
Jan 25, 2022
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c586920
12767907: Add a package dependency on Microsoft.RL
johnhuang01 Dec 11, 2021
43967d3
Add RankProcessor
johnhuang01 Dec 24, 2021
1e400a8
Remove unused file
johnhuang01 Dec 24, 2021
8e1be98
Remove some key
johnhuang01 Dec 24, 2021
7ec50ec
Toggle commented codes so that most tests can pass
johnhuang01 Jan 7, 2022
0d83604
Getting the required config details for rankprocessor to enable livemode
Tparuchuri Jan 13, 2022
7bef96f
Updating the wrong variable name
Tparuchuri Jan 13, 2022
db8c651
Cleanup
Tparuchuri Jan 14, 2022
d6df6b5
Revert "12767907: Add a package dependency on Microsoft.RL"
Tparuchuri Jan 14, 2022
70c9738
Revert "Add RankProcessor"
Tparuchuri Jan 14, 2022
ebbbdad
Revert "Remove unused file"
Tparuchuri Jan 14, 2022
9856a35
Revert "Toggle commented codes so that most tests can pass"
Tparuchuri Jan 14, 2022
4255840
Delete DisposeHelper.cs
Tparuchuri Jan 14, 2022
86532d3
Revert "Revert "12767907: Add a package dependency on Microsoft.RL""
Tparuchuri Jan 14, 2022
8156ddb
Merge branch 'ClientConfiguration' of https://github.com/johnhuang01/…
Tparuchuri Jan 14, 2022
c37c4d3
Cleanup after reverting
Tparuchuri Jan 14, 2022
6b75695
generating client configuration using autorest
Tparuchuri Jan 18, 2022
1f0a161
Revert "generating client configuration using autorest"
Tparuchuri Jan 20, 2022
59e364e
Configuration details for livemodel
Tparuchuri Jan 20, 2022
92f6ba3
cleanup
Tparuchuri Jan 20, 2022
2c7e79c
cleanup
Tparuchuri Jan 20, 2022
23d1c93
Added ToDo comments
Tparuchuri Jan 20, 2022
a914445
correct the version in endpoints
Tparuchuri Jan 21, 2022
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
@@ -0,0 +1,84 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Azure.Core;
using Azure.Core.Pipeline;

namespace Azure.AI.Personalizer.Models
{
internal class ClientConfigurationRestClient
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't this class in the generated folder? Rest clients should not be hand written, only extended.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As client Post is not part of swagger because of which it's not part of generated. So implemented it outside generated to get client config details

{
private string endpoint;
private ClientDiagnostics _clientDiagnostics;
private HttpPipeline _pipeline;

/// <summary> Initializes a new instance of ClientConfigurationRestClient. </summary>
/// <param name="clientDiagnostics"> The handler for diagnostic messaging in the client. </param>
/// <param name="pipeline"> The HTTP pipeline for sending and receiving REST requests and responses. </param>
/// <param name="endpoint"> Supported Cognitive Services endpoint. </param>
/// <exception cref="ArgumentNullException"> <paramref name="endpoint"/> is null. </exception>
public ClientConfigurationRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string endpoint)
{
this.endpoint = endpoint ?? throw new ArgumentNullException(nameof(endpoint));
_clientDiagnostics = clientDiagnostics;
_pipeline = pipeline;
}
internal HttpMessage CreatePostRequest()
{
var message = _pipeline.CreateMessage();
var request = message.Request;
request.Method = RequestMethod.Post;
var uri = new RawRequestUriBuilder();
uri.AppendRaw(endpoint, false);
uri.AppendRaw("/personalizer/v1.1-preview.1", false);
uri.AppendPath("/configurations/client", false);
request.Uri = uri;
request.Headers.Add("Accept", "application/json");
return message;
}

/// <summary> Get the Personalizer service configuration. </summary>
/// <param name="cancellationToken"> The cancellation token to use. </param>
public async Task<Response<PersonalizerClientProperties>> PostAsync(CancellationToken cancellationToken = default)
{
using var message = CreatePostRequest();
await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false);
switch (message.Response.Status)
{
case 200:
{
PersonalizerClientProperties value = default;
using var document = await JsonDocument.ParseAsync(message.Response.ContentStream, default, cancellationToken).ConfigureAwait(false);
value = PersonalizerClientProperties.DeserializePersonalizerServiceProperties(document.RootElement);
return Response.FromValue(value, message.Response);
}
default:
throw await _clientDiagnostics.CreateRequestFailedExceptionAsync(message.Response).ConfigureAwait(false);
}
}

/// <summary> Get the Personalizer service configuration. </summary>
/// <param name="cancellationToken"> The cancellation token to use. </param>
public Response<PersonalizerClientProperties> Post(CancellationToken cancellationToken = default)
{
using var message = CreatePostRequest();
_pipeline.Send(message, cancellationToken);
switch (message.Response.Status)
{
case 200:
{
PersonalizerClientProperties value = default;
using var document = JsonDocument.Parse(message.Response.ContentStream);
value = PersonalizerClientProperties.DeserializePersonalizerServiceProperties(document.RootElement);
return Response.FromValue(value, message.Response);
}
default:
throw _clientDiagnostics.CreateRequestFailedException(message.Response);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using Azure.Core;
Expand All @@ -22,6 +23,9 @@ public class PersonalizerClient
internal MultiSlotRestClient MultiSlotRestClient { get; set; }
internal MultiSlotEventsRestClient MultiSlotEventsRestClient { get; set; }

internal Models.ClientConfigurationRestClient ClientConfigurationRestClient { get; set; }
internal Lazy<Models.PersonalizerClientProperties> result { get; set; }

/// <summary> Initializes a new instance of Personalizer Client for mocking. </summary>
protected PersonalizerClient()
{
Expand Down Expand Up @@ -51,6 +55,7 @@ public PersonalizerClient(Uri endpoint, TokenCredential credential, Personalizer
EventsRestClient = new EventsRestClient(_clientDiagnostics, _pipeline, stringEndpoint);
MultiSlotRestClient = new MultiSlotRestClient(_clientDiagnostics, _pipeline, stringEndpoint);
MultiSlotEventsRestClient = new MultiSlotEventsRestClient(_clientDiagnostics, _pipeline, stringEndpoint);
ClientConfigurationRestClient = new Models.ClientConfigurationRestClient(_clientDiagnostics, _pipeline, stringEndpoint);
}

/// <summary> Initializes a new instance of PersonalizerClient. </summary>
Expand All @@ -62,6 +67,9 @@ public PersonalizerClient(Uri endpoint, TokenCredential credential, bool isLocal
this(endpoint, credential, options)
{
_isLocalInference = isLocalInference;
LiveModel liveModel = new LiveModel(configuration);
liveModel.Init();
_rankProcessor = new RankProcessor(liveModel);
}

/// <summary> Initializes a new instance of PersonalizerClient. </summary>
Expand Down Expand Up @@ -92,6 +100,7 @@ public PersonalizerClient(Uri endpoint, AzureKeyCredential credential, Personali
EventsRestClient = new EventsRestClient(_clientDiagnostics, _pipeline, stringEndpoint);
MultiSlotRestClient = new MultiSlotRestClient(_clientDiagnostics, _pipeline, stringEndpoint);
MultiSlotEventsRestClient = new MultiSlotEventsRestClient(_clientDiagnostics, _pipeline, stringEndpoint);
ClientConfigurationRestClient = new Models.ClientConfigurationRestClient(_clientDiagnostics, _pipeline, stringEndpoint);
}

/// <summary> Initializes a new instance of PersonalizerClient. </summary>
Expand All @@ -103,6 +112,12 @@ public PersonalizerClient(Uri endpoint, AzureKeyCredential credential, bool isLo
this(endpoint, credential, options)
{
_isLocalInference = isLocalInference;
if (isLocalInference)
{
LiveModel liveModel = new LiveModel(GetClientConfigurationForLiveModel());
liveModel.Init();
_rankProcessor = new RankProcessor(liveModel);
}
}

/// <summary> Initializes a new instance of PersonalizerClient. </summary>
Expand All @@ -121,6 +136,7 @@ internal PersonalizerClient(ClientDiagnostics clientDiagnostics, HttpPipeline pi
EventsRestClient = new EventsRestClient(_clientDiagnostics, _pipeline, stringEndpoint);
MultiSlotRestClient = new MultiSlotRestClient(_clientDiagnostics, _pipeline, stringEndpoint);
MultiSlotEventsRestClient = new MultiSlotEventsRestClient(_clientDiagnostics, _pipeline, stringEndpoint);
ClientConfigurationRestClient = new Models.ClientConfigurationRestClient(_clientDiagnostics, _pipeline, stringEndpoint);
_clientDiagnostics = clientDiagnostics;
_pipeline = pipeline;
}
Expand Down Expand Up @@ -177,7 +193,14 @@ public virtual Response<PersonalizerRankResult> Rank(PersonalizerRankOptions opt
scope.Start();
try
{
return RankRestClient.Rank(options, cancellationToken);
if (_isLocalInference)
{
return _rankProcessor.Rank(options, cancellationToken);
}
else
{
return RankRestClient.Rank(options, cancellationToken);
}
}
catch (Exception e)
{
Expand Down Expand Up @@ -482,5 +505,24 @@ public virtual Response ActivateMultiSlot(string eventId, CancellationToken canc
throw;
}
}

/// <summary> Gets the configuration details for the live model to use </summary>
internal Configuration GetClientConfigurationForLiveModel()
{
result = new Lazy<Models.PersonalizerClientProperties>(() => ClientConfigurationRestClient.Post());
Copy link
Collaborator

@tyclintw tyclintw Jan 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have a TTL in case the configuration on the server is updated.

Also, I would suggest the Lazy TTL be managed by the caller, not inside this method. In other words, the caller should use Lazy.

Console.WriteLine("InternalId: " + result.Value.ApplicationID + "\nStorageBlobUri: " + result.Value.ModelBlobUri + "\nLearningMode: " + result.Value.LearningMode + "\nExploration Percentage: " + result.Value.InitialExplorationEpsilon + "\ninitialCommandline: " + result.Value.InitialCommandLine);

Configuration config = new Configuration();
// configure the personalizer loop
config["appid"] = result.Value.ApplicationID;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of call result.Value half a dozen times, please use a temporary reference.


// set up the model
config["model.source"] = result.Value.ModelBlobUri;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be "model.blob.uri"

config["model.vw.initial_command_line"] = result.Value.InitialCommandLine;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be "vw.commandline"

config["initial_exploration.epsilon"] = Convert.ToString(result.Value.InitialExplorationEpsilon, CultureInfo.InvariantCulture);
config["rank.learning.mode"] = Convert.ToString(result.Value.LearningMode, CultureInfo.InvariantCulture);
//return the config model
return config;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Text.Json;
using Azure.Core;

namespace Azure.AI.Personalizer.Models
{
internal partial class PersonalizerClientProperties
{
internal static PersonalizerClientProperties DeserializePersonalizerServiceProperties(JsonElement element)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why we need a custom deserializer instead of using JsonSerializer.Deserialize<PersonalizerClientProperties>(string)?

{
string applicationID = default;
string modelBlobUri = default;
float initialExplorationEpsilon = default;
PersonalizerLearningMode learningMode = default;
string initialCommandLine = default;
foreach (var property in element.EnumerateObject())
{
if (property.NameEquals("applicationID"))
{
applicationID = property.Value.GetString();
continue;
}
if (property.NameEquals("modelBlobUri"))
{
modelBlobUri = property.Value.GetString();
continue;
}
if (property.NameEquals("initialExplorationEpsilon"))
{
initialExplorationEpsilon = property.Value.GetSingle();
continue;
}
if (property.NameEquals("learningMode"))
{
learningMode = new PersonalizerLearningMode(property.Value.GetString());
continue;
}
if (property.NameEquals("initialCommandLine"))
{
initialCommandLine = property.Value.GetString();
continue;
}
}
return new PersonalizerClientProperties(applicationID, modelBlobUri, initialExplorationEpsilon, learningMode, initialCommandLine);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Text;

namespace Azure.AI.Personalizer.Models
{
internal partial class PersonalizerClientProperties
{
internal PersonalizerClientProperties(string applicationID, string modelBlobUri, float initialExplorationEpsilon, PersonalizerLearningMode learningMode, string initialCommandLine)
{
ApplicationID = applicationID;
ModelBlobUri = modelBlobUri;
InitialExplorationEpsilon = initialExplorationEpsilon;
LearningMode = learningMode;
InitialCommandLine = initialCommandLine;
}
/// <summary>
/// Unique identifier for this Personalizer instance.
/// </summary>
public string ApplicationID { get; }

/// <summary>
/// Event hub connection string for sending interactions.
/// </summary>
public string EventHubInteractionConnectionString { get;}

/// <summary>
/// Event hub connection string for sending observations.
/// </summary>
public string EventHubObservationConnectionString { get; }

/// <summary>
/// SAS Uri for the inference model.
/// </summary>
public string ModelBlobUri { get; }

/// <summary>
/// Exploration value used before downloading model in CB.
/// </summary>
public float InitialExplorationEpsilon { get; }

/// <summary>
/// Learning Mode setting.
/// </summary>
public PersonalizerLearningMode LearningMode { get; }

/// <summary>
/// Command line used for prediction before downloading model.
/// </summary>
public string InitialCommandLine { get; }

/// <summary>
/// Version used by reinforcement learning client.
/// </summary>
public int ProtocolVersion { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,16 @@ protected async Task<PersonalizerClient> GetPersonalizerClientAsync(bool isSingl
}
var credential = new AzureKeyCredential(apiKey);
var options = InstrumentClientOptions(new PersonalizerClientOptions());
PersonalizerClient personalizerClient = new PersonalizerClient(new Uri(endpoint), credential, options);
PersonalizerClient personalizerClient = null;
if (isLocalInference)
{
personalizerClient = new PersonalizerClient(new Uri(endpoint), credential, true, options);
}
else
{
personalizerClient = new PersonalizerClient(new Uri(endpoint), credential, options);
}

personalizerClient = InstrumentClient(personalizerClient);
return personalizerClient;
}
Expand Down