diff --git a/sdk/personalizer/Azure.AI.Personalizer/api/Azure.AI.Personalizer.netstandard2.0.cs b/sdk/personalizer/Azure.AI.Personalizer/api/Azure.AI.Personalizer.netstandard2.0.cs index 1c28136a47a1..47bcdceab0fd 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/api/Azure.AI.Personalizer.netstandard2.0.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/api/Azure.AI.Personalizer.netstandard2.0.cs @@ -1,81 +1,5 @@ namespace Azure.AI.Personalizer { - public partial class ActionProbabilityWrapper - { - public ActionProbabilityWrapper() { } - public ActionProbabilityWrapper(Rl.Net.ActionProbability actionProbability) { } - public virtual long ActionIndex { get { throw null; } } - public virtual float Probability { get { throw null; } } - } - public partial class DecisionContext - { - public DecisionContext() { } - public DecisionContext(Azure.AI.Personalizer.PersonalizerRankMultiSlotOptions rankRequest, System.Collections.Generic.Dictionary> slotIdToFeatures) { } - public DecisionContext(System.Collections.Generic.IEnumerable contextFeatures, System.Collections.Generic.List rankableActions) { } - [System.Text.Json.Serialization.JsonIgnoreAttribute(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] - [System.Text.Json.Serialization.JsonPropertyNameAttribute("FromUrl")] - public System.Collections.Generic.List ContextFeatures { get { throw null; } } - [System.Text.Json.Serialization.JsonPropertyNameAttribute("_multi")] - public System.Collections.Generic.IList Documents { get { throw null; } } - [System.Text.Json.Serialization.JsonIgnoreAttribute(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] - [System.Text.Json.Serialization.JsonPropertyNameAttribute("_slots")] - public System.Collections.Generic.IList Slots { get { throw null; } } - } - public partial class DecisionContextDocument - { - public DecisionContextDocument(string id, System.Collections.Generic.List actionFeatureJsonList, string slotId, System.Collections.Generic.List slotFeatureJsonList) { } - [System.Text.Json.Serialization.JsonIgnoreAttribute(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] - [System.Text.Json.Serialization.JsonPropertyNameAttribute("j")] - public System.Collections.Generic.List ActionFeatureJsonList { get { throw null; } } - [System.Text.Json.Serialization.JsonIgnoreAttribute(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] - [System.Text.Json.Serialization.JsonPropertyNameAttribute("_tag")] - public string Id { get { throw null; } } - [System.Text.Json.Serialization.JsonIgnoreAttribute(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] - [System.Text.Json.Serialization.JsonPropertyNameAttribute("sj")] - public System.Collections.Generic.List SlotFeatureJsonList { get { throw null; } } - [System.Text.Json.Serialization.JsonIgnoreAttribute(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] - [System.Text.Json.Serialization.JsonPropertyNameAttribute("_id")] - public string SlotId { get { throw null; } } - } - public partial class EventResponse : Azure.Response - { - public EventResponse(int status, string reasonPhrase = null) { } - public override string ClientRequestId { get { throw null; } set { } } - public override System.IO.Stream ContentStream { get { throw null; } set { } } - public override string ReasonPhrase { get { throw null; } } - public override int Status { get { throw null; } } - protected override bool ContainsHeader(string name) { throw null; } - public override void Dispose() { } - protected override System.Collections.Generic.IEnumerable EnumerateHeaders() { throw null; } - protected override bool TryGetHeader(string name, out string value) { throw null; } - protected override bool TryGetHeaderValues(string name, out System.Collections.Generic.IEnumerable values) { throw null; } - } - public partial interface ILiveModel - { - Azure.AI.Personalizer.RankingResponseWrapper ChooseRank(string eventId, string contextJson, Rl.Net.ActionFlags flags); - void Init(); - void QueueActionTakenEvent(string eventId); - void QueueOutcomeEvent(string eventId, float outcome); - void QueueOutcomeEvent(string eventId, string slotId, float outcome); - Azure.AI.Personalizer.MultiSlotResponseDetailedWrapper RequestMultiSlotDecisionDetailed(string eventId, string contextJson, Rl.Net.ActionFlags flags, int[] baselineActions); - } - public partial class LiveModelAdapter : Azure.AI.Personalizer.ILiveModel - { - public LiveModelAdapter(Rl.Net.LiveModel liveModel) { } - public Azure.AI.Personalizer.RankingResponseWrapper ChooseRank(string eventId, string contextJson, Rl.Net.ActionFlags flags) { throw null; } - public void Init() { } - public void QueueActionTakenEvent(string eventId) { } - public void QueueOutcomeEvent(string eventId, float outcome) { } - public void QueueOutcomeEvent(string eventId, string slotId, float outcome) { } - public Azure.AI.Personalizer.MultiSlotResponseDetailedWrapper RequestMultiSlotDecisionDetailed(string eventId, string contextJson, Rl.Net.ActionFlags flags, int[] baselineActions) { throw null; } - } - public partial class MultiSlotResponseDetailedWrapper : System.Collections.Generic.IEnumerable, System.Collections.IEnumerable - { - public MultiSlotResponseDetailedWrapper() { } - public MultiSlotResponseDetailedWrapper(Rl.Net.MultiSlotResponseDetailed multiSlotResponse) { } - public virtual System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } - } public partial class PersonalizerAdministrationClient { protected PersonalizerAdministrationClient() { } @@ -119,10 +43,8 @@ public partial class PersonalizerClient protected PersonalizerClient() { } public PersonalizerClient(System.Uri endpoint, Azure.AzureKeyCredential credential) { } public PersonalizerClient(System.Uri endpoint, Azure.AzureKeyCredential credential, Azure.AI.Personalizer.PersonalizerClientOptions options = null) { } - public PersonalizerClient(System.Uri endpoint, Azure.AzureKeyCredential credential, bool isLocalInference, float subsampleRate = 1f, Azure.AI.Personalizer.PersonalizerClientOptions options = null) { } public PersonalizerClient(System.Uri endpoint, Azure.Core.TokenCredential credential) { } public PersonalizerClient(System.Uri endpoint, Azure.Core.TokenCredential credential, Azure.AI.Personalizer.PersonalizerClientOptions options = null) { } - public PersonalizerClient(System.Uri endpoint, Azure.Core.TokenCredential credential, bool isLocalInference, float subsampleRate = 1f, Azure.AI.Personalizer.PersonalizerClientOptions options = null) { } public virtual Azure.Response Activate(string eventId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task ActivateAsync(string eventId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response ActivateMultiSlot(string eventId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -144,10 +66,10 @@ public PersonalizerClient(System.Uri endpoint, Azure.Core.TokenCredential creden } public partial class PersonalizerClientOptions : Azure.Core.ClientOptions { - public PersonalizerClientOptions(Azure.AI.Personalizer.PersonalizerClientOptions.ServiceVersion version = Azure.AI.Personalizer.PersonalizerClientOptions.ServiceVersion.V1_1_preview_1) { } + public PersonalizerClientOptions(Azure.AI.Personalizer.PersonalizerClientOptions.ServiceVersion version = Azure.AI.Personalizer.PersonalizerClientOptions.ServiceVersion.V1_1_preview_3, bool isLocalInference = false, float subsampleRate = 1f) { } public enum ServiceVersion { - V1_1_preview_1 = 1, + V1_1_preview_3 = 1, } } public partial class PersonalizerCreateEvaluationOperation : Azure.Operation @@ -371,7 +293,7 @@ public partial class PersonalizerRankResult { internal PersonalizerRankResult() { } public string EventId { get { throw null; } } - public System.Collections.Generic.IReadOnlyList Ranking { get { throw null; } set { } } + public System.Collections.Generic.IReadOnlyList Ranking { get { throw null; } } public string RewardActionId { get { throw null; } } } public partial class PersonalizerRewardMultiSlotOptions @@ -423,21 +345,4 @@ public PersonalizerSlotReward(string slotId, float value) { } public string SlotId { get { throw null; } } public float Value { get { throw null; } } } - public partial class RankingResponseWrapper : System.Collections.Generic.IEnumerable, System.Collections.IEnumerable - { - public RankingResponseWrapper() { } - public RankingResponseWrapper(Rl.Net.RankingResponse rankResponse) { } - public virtual System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } - } - public partial class SlotRankingResponseWrapper : System.Collections.Generic.IEnumerable, System.Collections.IEnumerable - { - public SlotRankingResponseWrapper() { } - public SlotRankingResponseWrapper(Rl.Net.SlotRanking slotRanking) { } - public virtual long ChosenAction { get { throw null; } } - public virtual long Count { get { throw null; } } - public virtual string SlotId { get { throw null; } } - public virtual System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } - } } diff --git a/sdk/personalizer/Azure.AI.Personalizer/src/Generated/Models/PersonalizerRankResult.cs b/sdk/personalizer/Azure.AI.Personalizer/src/Generated/Models/PersonalizerRankResult.cs index 9eb32219a975..bfa9a6de6ccb 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/src/Generated/Models/PersonalizerRankResult.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/src/Generated/Models/PersonalizerRankResult.cs @@ -35,7 +35,7 @@ internal PersonalizerRankResult(IReadOnlyList ranking, } /// The calculated ranking for the current request. - public IReadOnlyList Ranking { get; set; } + public IReadOnlyList Ranking { get; } /// The eventId for the round trip from request to response. public string EventId { get; } diff --git a/sdk/personalizer/Azure.AI.Personalizer/src/Generated/RankClient.cs b/sdk/personalizer/Azure.AI.Personalizer/src/Generated/RankClient.cs deleted file mode 100644 index f5a56abb28f3..000000000000 --- a/sdk/personalizer/Azure.AI.Personalizer/src/Generated/RankClient.cs +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -// - -#nullable disable - -using System; -using System.Threading; -using System.Threading.Tasks; -using Azure; -using Azure.Core; -using Azure.Core.Pipeline; -using Rl.Net; - -namespace Azure.AI.Personalizer -{ - /// The Rank service client. - internal partial class RankClient - { - private readonly ClientDiagnostics _clientDiagnostics; - private readonly HttpPipeline _pipeline; - private readonly bool _isLocalInference; - private readonly RlNetProcessor _rlNetProcessor; - internal RankRestClient RestClient { get; } - - /// Initializes a new instance of RankClient for mocking. - protected RankClient() - { - } - - /// Initializes a new instance of RankClient. - /// Supported Cognitive Services endpoint. - /// A credential used to authenticate to an Azure Service. - /// The options for configuring the client. - public RankClient(string endpoint, TokenCredential credential, PersonalizerClientOptions options = null) - { - if (endpoint == null) - { - throw new ArgumentNullException(nameof(endpoint)); - } - if (credential == null) - { - throw new ArgumentNullException(nameof(credential)); - } - - options ??= new PersonalizerClientOptions(); - _clientDiagnostics = new ClientDiagnostics(options); - string[] scopes = { "https://cognitiveservices.azure.com/.default" }; - _pipeline = HttpPipelineBuilder.Build(options, new BearerTokenAuthenticationPolicy(credential, scopes)); - RestClient = new RankRestClient(_clientDiagnostics, _pipeline, endpoint); - } - - /// Initializes a new instance of PersonalizerClient. - /// Supported Cognitive Services endpoint. - /// A credential used to authenticate to an Azure Service. - /// A flag to determine whether to use local inference. - /// A configuration to use local reference. - /// The options for configuring the client. - public RankClient(string endpoint, TokenCredential credential, bool isLocalInference = false, Configuration configuration = null, PersonalizerClientOptions options = null) : - this(endpoint, credential, options) - { - _isLocalInference = isLocalInference; - if (isLocalInference) - { - LiveModel liveModel = new LiveModel(configuration); - liveModel.Init(); - ILiveModel liveModelAdapter = new LiveModelAdapter(liveModel); - _rlNetProcessor = new RlNetProcessor(liveModelAdapter); - } - } - - /// Initializes a new instance of RankClient. - /// Supported Cognitive Services endpoint. - /// A credential used to authenticate to an Azure Service. - /// The options for configuring the client. - public RankClient(string endpoint, AzureKeyCredential credential, PersonalizerClientOptions options = null) - { - if (endpoint == null) - { - throw new ArgumentNullException(nameof(endpoint)); - } - if (credential == null) - { - throw new ArgumentNullException(nameof(credential)); - } - - options ??= new PersonalizerClientOptions(); - _clientDiagnostics = new ClientDiagnostics(options); - _pipeline = HttpPipelineBuilder.Build(options, new AzureKeyCredentialPolicy(credential, "Ocp-Apim-Subscription-Key")); - RestClient = new RankRestClient(_clientDiagnostics, _pipeline, endpoint); - } - - /// Initializes a new instance of PersonalizerClient. - /// Supported Cognitive Services endpoint. - /// A credential used to authenticate to an Azure Service. - /// A flag to determine whether to use local reference. - /// A configuration to use local reference. - /// The options for configuring the client. - public RankClient(string endpoint, AzureKeyCredential credential, bool isLocalInference = false, Configuration configuration = null, PersonalizerClientOptions options = null) : - this(endpoint, credential, options) - { - _isLocalInference = isLocalInference; - if (isLocalInference) - { - LiveModel liveModel = new LiveModel(configuration); - liveModel.Init(); - LiveModelAdapter liveModelAdapter = new LiveModelAdapter(liveModel); - _rlNetProcessor = new RlNetProcessor(liveModelAdapter); - } - - } - - /// Initializes a new instance of RankClient. - /// The handler for diagnostic messaging in the client. - /// The HTTP pipeline for sending and receiving REST requests and responses. - /// Supported Cognitive Services endpoint. - internal RankClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string endpoint) - { - RestClient = new RankRestClient(clientDiagnostics, pipeline, endpoint); - _clientDiagnostics = clientDiagnostics; - _pipeline = pipeline; - } - - /// Submit a Personalizer rank request. Receives a context and a list of actions. Returns which of the provided actions should be used by your application, in rewardActionId. - /// A Personalizer Rank request. - /// The cancellation token to use. - public virtual async Task> RankAsync(PersonalizerRankOptions rankRequest, CancellationToken cancellationToken = default) - { - using var scope = _clientDiagnostics.CreateScope("RankClient.Rank"); - scope.Start(); - try - { - if (_isLocalInference) - { - return _rlNetProcessor.Rank(rankRequest); - } - else - { - return await RestClient.RankAsync(rankRequest, cancellationToken).ConfigureAwait(false); - } - } - catch (Exception e) - { - scope.Failed(e); - throw; - } - } - - /// Submit a Personalizer rank request. Receives a context and a list of actions. Returns which of the provided actions should be used by your application, in rewardActionId. - /// A Personalizer Rank request. - /// The cancellation token to use. - public virtual Response Rank(PersonalizerRankOptions rankRequest, CancellationToken cancellationToken = default) - { - using var scope = _clientDiagnostics.CreateScope("RankClient.Rank"); - scope.Start(); - try - { - return RestClient.Rank(rankRequest, cancellationToken); - } - catch (Exception e) - { - scope.Failed(e); - throw; - } - } - } -} diff --git a/sdk/personalizer/Azure.AI.Personalizer/src/Models/ActionProbabilityWrapper.cs b/sdk/personalizer/Azure.AI.Personalizer/src/Models/ActionProbabilityWrapper.cs index 53b51e4deeb1..2a6fc2321b28 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/src/Models/ActionProbabilityWrapper.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/src/Models/ActionProbabilityWrapper.cs @@ -6,7 +6,7 @@ namespace Azure.AI.Personalizer { /// The Wrapper for Rl.Net.ActionProbability - public class ActionProbabilityWrapper + internal class ActionProbabilityWrapper { private readonly ActionProbability _actionProbability; diff --git a/sdk/personalizer/Azure.AI.Personalizer/src/Models/DecisionContext.cs b/sdk/personalizer/Azure.AI.Personalizer/src/Models/DecisionContext.cs index a3f302278da1..87f4567390f8 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/src/Models/DecisionContext.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/src/Models/DecisionContext.cs @@ -9,7 +9,7 @@ namespace Azure.AI.Personalizer { /// The Decision Context. - public class DecisionContext + internal class DecisionContext { /// The Decision Context used to serialize an object. public DecisionContext() diff --git a/sdk/personalizer/Azure.AI.Personalizer/src/Models/DecisionContextDocument.cs b/sdk/personalizer/Azure.AI.Personalizer/src/Models/DecisionContextDocument.cs index 51c2d16161dd..4a4d7e404e39 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/src/Models/DecisionContextDocument.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/src/Models/DecisionContextDocument.cs @@ -7,19 +7,19 @@ namespace Azure.AI.Personalizer { /// The Decision Context Document. - public class DecisionContextDocument + internal class DecisionContextDocument { /// Initializes a new instance of DecisionContextDocument. /// Id of the decision context document - /// The json list of action features + /// The json list of action features /// The slot Id - /// The json list of slot features - public DecisionContextDocument(string id, List actionFeatureJsonList, string slotId, List slotFeatureJsonList) + /// The json list of slot features + public DecisionContextDocument(string id, List actionFeatures, string slotId, List slotFeatures) { Id = id; - ActionFeatureJsonList = actionFeatureJsonList; + ActionFeatures = actionFeatures; SlotId = slotId; - SlotFeatureJsonList = slotFeatureJsonList; + SlotFeatures = slotFeatures; } /// @@ -35,7 +35,7 @@ public DecisionContextDocument(string id, List actionFeatureJsonList, st [JsonPropertyName("j")] [JsonConverter(typeof(JsonRawStringListConverter))] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public List ActionFeatureJsonList { get; } + public List ActionFeatures { get; } /// /// Slot ID. @@ -50,6 +50,6 @@ public DecisionContextDocument(string id, List actionFeatureJsonList, st [JsonPropertyName("sj")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonConverter(typeof(JsonRawStringListConverter))] - public List SlotFeatureJsonList { get; } + public List SlotFeatures { get; } } } diff --git a/sdk/personalizer/Azure.AI.Personalizer/src/Models/EventResponse.cs b/sdk/personalizer/Azure.AI.Personalizer/src/Models/EventResponse.cs index 41ebad8d525e..50d709ff3fcc 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/src/Models/EventResponse.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/src/Models/EventResponse.cs @@ -11,7 +11,7 @@ namespace Azure.AI.Personalizer { /// Response of an event operation - public class EventResponse : Response + internal class EventResponse : Response { private readonly Dictionary> _headers = new Dictionary>(StringComparer.OrdinalIgnoreCase); diff --git a/sdk/personalizer/Azure.AI.Personalizer/src/Models/ILiveModel.cs b/sdk/personalizer/Azure.AI.Personalizer/src/Models/ILiveModel.cs deleted file mode 100644 index 4ad02796042b..000000000000 --- a/sdk/personalizer/Azure.AI.Personalizer/src/Models/ILiveModel.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Rl.Net; -using System; - -namespace Azure.AI.Personalizer -{ - /// An interface for Rl.Net.LiveModel - public interface ILiveModel - { - /// Init LiveModel - void Init(); - - /// Wrapper method of ChooseRank - RankingResponseWrapper ChooseRank(string eventId, string contextJson, ActionFlags flags); - - /// Wrapper method of RequestMultiSlotDecisionDetailed - MultiSlotResponseDetailedWrapper RequestMultiSlotDecisionDetailed(string eventId, string contextJson, ActionFlags flags, int[] baselineActions); - - /// Wrapper method of QueueOutcomeEvent - void QueueOutcomeEvent(string eventId, float outcome); - - /// Wrapper method of RequestMultiSlotDecisionDetailed - void QueueOutcomeEvent(string eventId, string slotId, float outcome); - - /// Wrapper method of QueueActionTakenEvent - void QueueActionTakenEvent(string eventId); - } -} diff --git a/sdk/personalizer/Azure.AI.Personalizer/src/Models/LiveModelAdapter.cs b/sdk/personalizer/Azure.AI.Personalizer/src/Models/LiveModelAdapter.cs index 81a1edbecd98..120ef15b56d1 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/src/Models/LiveModelAdapter.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/src/Models/LiveModelAdapter.cs @@ -7,33 +7,33 @@ namespace Azure.AI.Personalizer { /// An adapter class of Rl.Net.LiveModel - public class LiveModelAdapter : ILiveModel + internal class LiveModelAdapter : LiveModelBase { private readonly LiveModel liveModel; /// Initializes a new instance of LiveModelAdapter. - public LiveModelAdapter(LiveModel liveModel) + internal LiveModelAdapter(LiveModel liveModel) { this.liveModel = liveModel ?? throw new ArgumentNullException(nameof(liveModel)); } /// Init LiveModel - public void Init() + public override void Init() { liveModel.Init(); } /// Wrapper method of ChooseRank - public RankingResponseWrapper ChooseRank(string eventId, string contextJson, ActionFlags flags) + public override RankingResponseWrapper ChooseRank(string eventId, string contextJson, ActionFlags actionFlags) { - RankingResponse rankingResponse = liveModel.ChooseRank(eventId, contextJson, flags); + RankingResponse rankingResponse = liveModel.ChooseRank(eventId, contextJson, actionFlags); RankingResponseWrapper rankingResponseWrapper = rankingResponse == null ? null : new RankingResponseWrapper(rankingResponse); return rankingResponseWrapper; } /// Wrapper method of RequestMultiSlotDecisionDetailed - public MultiSlotResponseDetailedWrapper RequestMultiSlotDecisionDetailed(string eventId, string contextJson, ActionFlags flags, int[] baselineActions) + public override MultiSlotResponseDetailedWrapper RequestMultiSlotDecisionDetailed(string eventId, string contextJson, ActionFlags flags, int[] baselineActions) { MultiSlotResponseDetailed multiSlotResponse = liveModel.RequestMultiSlotDecisionDetailed(eventId, contextJson, flags, baselineActions); MultiSlotResponseDetailedWrapper multiSlotResponseDetailedWrapper = multiSlotResponse == null ? null : new MultiSlotResponseDetailedWrapper(multiSlotResponse); @@ -41,19 +41,19 @@ public MultiSlotResponseDetailedWrapper RequestMultiSlotDecisionDetailed(string } /// Wrapper method of QueueOutcomeEvent - public void QueueOutcomeEvent(string eventId, float outcome) + public override void QueueOutcomeEvent(string eventId, float outcome) { liveModel.QueueOutcomeEvent(eventId, outcome); } /// Wrapper method of RequestMultiSlotDecisionDetailed - public void QueueOutcomeEvent(string eventId, string slotId, float outcome) + public override void QueueOutcomeEvent(string eventId, string slotId, float outcome) { liveModel.QueueOutcomeEvent(eventId, slotId, outcome); } /// Wrapper method of QueueActionTakenEvent - public void QueueActionTakenEvent(string eventId) + public override void QueueActionTakenEvent(string eventId) { liveModel.QueueActionTakenEvent(eventId); } diff --git a/sdk/personalizer/Azure.AI.Personalizer/src/Models/LiveModelBase.cs b/sdk/personalizer/Azure.AI.Personalizer/src/Models/LiveModelBase.cs new file mode 100644 index 000000000000..90233361afee --- /dev/null +++ b/sdk/personalizer/Azure.AI.Personalizer/src/Models/LiveModelBase.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Rl.Net; +using System; + +namespace Azure.AI.Personalizer +{ + /// An abstract class for Rl.Net.LiveModel + internal abstract class LiveModelBase + { + /// Init LiveModel + public abstract void Init(); + + /// Wrapper method of ChooseRank + public abstract RankingResponseWrapper ChooseRank(string eventId, string contextJson, ActionFlags flags); + + /// Wrapper method of RequestMultiSlotDecisionDetailed + public abstract MultiSlotResponseDetailedWrapper RequestMultiSlotDecisionDetailed(string eventId, string contextJson, ActionFlags flags, int[] baselineActions); + + /// Wrapper method of QueueOutcomeEvent + public abstract void QueueOutcomeEvent(string eventId, float outcome); + + /// Wrapper method of RequestMultiSlotDecisionDetailed + public abstract void QueueOutcomeEvent(string eventId, string slotId, float outcome); + + /// Wrapper method of QueueActionTakenEvent + public abstract void QueueActionTakenEvent(string eventId); + } +} diff --git a/sdk/personalizer/Azure.AI.Personalizer/src/Models/MultiSlotResponseDetailedWrapper.cs b/sdk/personalizer/Azure.AI.Personalizer/src/Models/MultiSlotResponseDetailedWrapper.cs index 86846431563f..5dab77a6c28b 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/src/Models/MultiSlotResponseDetailedWrapper.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/src/Models/MultiSlotResponseDetailedWrapper.cs @@ -9,7 +9,7 @@ namespace Azure.AI.Personalizer { /// The Wrapper for Rl.Net.MultiSlotResponseDetailed - public class MultiSlotResponseDetailedWrapper : IEnumerable + internal class MultiSlotResponseDetailedWrapper : IEnumerable { private readonly MultiSlotResponseDetailed _multiSlotResponse; @@ -25,12 +25,12 @@ public MultiSlotResponseDetailedWrapper(MultiSlotResponseDetailed multiSlotRespo } /// Get the enumerator - public virtual IEnumerator GetEnumerator() + public virtual IEnumerator GetEnumerator() { var enu = _multiSlotResponse.GetEnumerator(); while (enu.MoveNext()) { - yield return new SlotRankingResponseWrapper(enu.Current); + yield return new SlotRankingWrapper(enu.Current); } } diff --git a/sdk/personalizer/Azure.AI.Personalizer/src/Models/PersonalizerClient.cs b/sdk/personalizer/Azure.AI.Personalizer/src/Models/PersonalizerClient.cs index f4c236bde5bb..582ef123a22c 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/src/Models/PersonalizerClient.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/src/Models/PersonalizerClient.cs @@ -69,21 +69,11 @@ public PersonalizerClient(Uri endpoint, TokenCredential credential, Personalizer ServiceConfigurationRestClient = new ServiceConfigurationRestClient(clientDiagnostics, pipeline, stringEndpoint); PolicyRestClient = new PolicyRestClient(clientDiagnostics, pipeline, stringEndpoint); tokenCredential = credential; - } - /// Initializes a new instance of PersonalizerClient. - /// Supported Cognitive Services endpoint. - /// A credential used to authenticate to an Azure Service. - /// A flag to determine whether to use local inference. - /// Percentage from (0,1] determines how much percentage of interaction and observation events to consider - /// The options for configuring the client. - public PersonalizerClient(Uri endpoint, TokenCredential credential, bool isLocalInference, float subsampleRate = 1.0f, PersonalizerClientOptions options = null) : - this(endpoint, credential, options) - { - this.isLocalInference = isLocalInference; + this.isLocalInference = options.IsLocalInference; if (isLocalInference) { - validateAndAssignSampleRate(subsampleRate); + validateAndAssignSampleRate(options.SubsampleRate); //lazy load Rankprocessor rlNetProcessor = new Lazy(() => GetConfigurationForRankProcessor()); } @@ -119,21 +109,11 @@ public PersonalizerClient(Uri endpoint, AzureKeyCredential credential, Personali ServiceConfigurationRestClient = new ServiceConfigurationRestClient(clientDiagnostics, pipeline, stringEndpoint); PolicyRestClient = new PolicyRestClient(clientDiagnostics, pipeline, stringEndpoint); azureKeyCredential = credential; - } - /// Initializes a new instance of PersonalizerClient. - /// Supported Cognitive Services endpoint. - /// A credential used to authenticate to an Azure Service. - /// A flag to determine whether to use local inference. - /// Percentage from (0,1] determines how much percentage of interaction and observation events to consider - /// The options for configuring the client. - public PersonalizerClient(Uri endpoint, AzureKeyCredential credential, bool isLocalInference, float subsampleRate = 1.0f, PersonalizerClientOptions options = null) : - this(endpoint, credential, options) - { - this.isLocalInference = isLocalInference; + this.isLocalInference = options.IsLocalInference; if (isLocalInference) { - validateAndAssignSampleRate(subsampleRate); + validateAndAssignSampleRate(options.SubsampleRate); //lazy load Rankprocessor rlNetProcessor = new Lazy(() => GetConfigurationForRankProcessor()); } @@ -653,7 +633,7 @@ internal virtual RlNetProcessor GetConfigurationForRankProcessor(CancellationTok config["rank.learning.mode"] = Convert.ToString(personalizerServiceProperties.LearningMode, CultureInfo.InvariantCulture); LiveModel liveModel = new LiveModel(config); liveModel.Init(); - ILiveModel liveModelAdapter = new LiveModelAdapter(liveModel); + LiveModelBase liveModelAdapter = new LiveModelAdapter(liveModel); liveModelLastRefresh = DateTimeOffset.UtcNow; return new RlNetProcessor(liveModelAdapter); } diff --git a/sdk/personalizer/Azure.AI.Personalizer/src/Generated/PersonalizerClientOptions.cs b/sdk/personalizer/Azure.AI.Personalizer/src/Models/PersonalizerClientOptions.cs similarity index 59% rename from sdk/personalizer/Azure.AI.Personalizer/src/Generated/PersonalizerClientOptions.cs rename to sdk/personalizer/Azure.AI.Personalizer/src/Models/PersonalizerClientOptions.cs index 643a4e1a15c8..1331f68cd215 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/src/Generated/PersonalizerClientOptions.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/src/Models/PersonalizerClientOptions.cs @@ -13,27 +13,35 @@ namespace Azure.AI.Personalizer /// Client options for PersonalizerClient. public partial class PersonalizerClientOptions : ClientOptions { - private const ServiceVersion LatestVersion = ServiceVersion.V1_1_preview_1; + private bool isLocalInference; + private float subsampleRate; + private const ServiceVersion LatestVersion = ServiceVersion.V1_1_preview_3; /// The version of the service to use. public enum ServiceVersion { - /// Service version "1.1-preview.1". - V1_1_preview_1 = 1, + /// Service version "v1.1-preview.3". + V1_1_preview_3 = 1, } internal string Version { get; } - /// Initializes new instance of PersonalizerClientOptions. - public PersonalizerClientOptions(ServiceVersion version = LatestVersion) + /// Initializes new instance of PersonalizerClientV1Preview3ClientOptions. + public PersonalizerClientOptions(ServiceVersion version = LatestVersion, bool isLocalInference = false, float subsampleRate = 1) { + this.isLocalInference = isLocalInference; + this.subsampleRate = subsampleRate; + Version = version switch { - ServiceVersion.V1_1_preview_1 => "1.1-preview.1", + ServiceVersion.V1_1_preview_3 => "v1.1-preview.3", _ => throw new NotSupportedException() }; Retry.NetworkTimeout = TimeSpan.FromMinutes(8); } + + internal float SubsampleRate { get => subsampleRate; } + internal bool IsLocalInference { get => isLocalInference; } } } diff --git a/sdk/personalizer/Azure.AI.Personalizer/src/Models/RankClient.cs b/sdk/personalizer/Azure.AI.Personalizer/src/Models/RankClient.cs deleted file mode 100644 index fdd734a83b92..000000000000 --- a/sdk/personalizer/Azure.AI.Personalizer/src/Models/RankClient.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Azure.Core; - -namespace Azure.AI.Personalizer -{ - [CodeGenClient("PersonalizerClient")] - internal partial class RankClient {} -} diff --git a/sdk/personalizer/Azure.AI.Personalizer/src/Models/RankingResponseWrapper.cs b/sdk/personalizer/Azure.AI.Personalizer/src/Models/RankingResponseWrapper.cs index 0d5b0014432a..d081fd900491 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/src/Models/RankingResponseWrapper.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/src/Models/RankingResponseWrapper.cs @@ -9,7 +9,7 @@ namespace Azure.AI.Personalizer { /// A wrapper class of Rl.Net.RankingResponse - public class RankingResponseWrapper : IEnumerable + internal class RankingResponseWrapper : IEnumerable { private readonly RankingResponse _rankingResponse; diff --git a/sdk/personalizer/Azure.AI.Personalizer/src/Models/RlNetProcessor.cs b/sdk/personalizer/Azure.AI.Personalizer/src/Models/RlNetProcessor.cs index 0463554b40da..088f1eaca3c7 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/src/Models/RlNetProcessor.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/src/Models/RlNetProcessor.cs @@ -14,11 +14,11 @@ namespace Azure.AI.Personalizer /// The Rl.Net Processor. internal class RlNetProcessor { - private readonly ILiveModel liveModel; + private readonly LiveModelBase liveModel; internal PolicyRestClient RestClient { get; } /// Initializes a new instance of RlNetProcessor. - public RlNetProcessor(ILiveModel liveModel) + public RlNetProcessor(LiveModelBase liveModel) { this.liveModel = liveModel; } diff --git a/sdk/personalizer/Azure.AI.Personalizer/src/Models/RlObjectConverter.cs b/sdk/personalizer/Azure.AI.Personalizer/src/Models/RlObjectConverter.cs index cfdba535e44b..941bff7dcbea 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/src/Models/RlObjectConverter.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/src/Models/RlObjectConverter.cs @@ -37,13 +37,11 @@ public static PersonalizerRankResult GenerateRankResult(List originalActions, - List rankableActions, List excludedActions, int[] rankedIndices, float[] rankingProbabilities, string eventId, int multiSlotChosenActionIndex = -1) + List rankableActions, List excludedActions, int[] rankedIndices, float[] rankingProbabilities, string eventId) { // excluded actions are not passed into VW // rankedIndices[0] is the index of the VW chosen action (1 based index) - // ccb response that is converted into a cb response: the chosen action index is a field in the vw response. - // multiSlotChosenActionIndex is part of the multi slot response (0 based index) - int chosenActionIndex = multiSlotChosenActionIndex == -1 ? rankedIndices[0] - 1 : multiSlotChosenActionIndex; + int chosenActionIndex = rankedIndices[0] - 1; // take care of actions that are excluded in their original positions if (excludedActions != null && excludedActions.Count > 0) @@ -69,14 +67,14 @@ private static PersonalizerRankResult GenerateRankResultInner(List + var rankings = rankedIndices?.Select((index, i) => { var action = originalActions[index - 1]; return new PersonalizerRankedAction(action.Id, rankingProbabilities[i]); }).ToList(); // setting RewardActionId to be the VW chosen action. - var personalizerRankResult = new PersonalizerRankResult(ranking, eventId, originalActions.ElementAt(chosenActionIndex)?.Id); + var personalizerRankResult = new PersonalizerRankResult(rankings, eventId, rankableActions.ElementAt(chosenActionIndex)?.Id); return personalizerRankResult; } diff --git a/sdk/personalizer/Azure.AI.Personalizer/src/Models/SlotRankingResponseWrapper.cs b/sdk/personalizer/Azure.AI.Personalizer/src/Models/SlotRankingWrapper.cs similarity index 84% rename from sdk/personalizer/Azure.AI.Personalizer/src/Models/SlotRankingResponseWrapper.cs rename to sdk/personalizer/Azure.AI.Personalizer/src/Models/SlotRankingWrapper.cs index 9e806ca4eada..de48f5b3da27 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/src/Models/SlotRankingResponseWrapper.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/src/Models/SlotRankingWrapper.cs @@ -8,17 +8,17 @@ namespace Azure.AI.Personalizer { - /// The Wrapper for Rl.Net.SlotRankingResponse - public class SlotRankingResponseWrapper : IEnumerable + /// The Wrapper for Rl.Net.SlotRanking + internal class SlotRankingWrapper : IEnumerable { private readonly SlotRanking _slotRanking; /// Initializes a new instance of SlotRankingResponseWrapper. - public SlotRankingResponseWrapper() + public SlotRankingWrapper() {} /// Initializes a new instance of SlotRankingResponseWrapper. - public SlotRankingResponseWrapper(SlotRanking slotRanking) + public SlotRankingWrapper(SlotRanking slotRanking) { _slotRanking = slotRanking ?? throw new ArgumentNullException(nameof(slotRanking)); } diff --git a/sdk/personalizer/Azure.AI.Personalizer/src/Properties/AssemblyInfo.cs b/sdk/personalizer/Azure.AI.Personalizer/src/Properties/AssemblyInfo.cs index 761a8aba1112..2e1a71977d10 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/src/Properties/AssemblyInfo.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/src/Properties/AssemblyInfo.cs @@ -6,4 +6,5 @@ [assembly: InternalsVisibleTo( "Azure.AI.Personalizer.Tests," + " PublicKey=0024000004800000940000000602000000240000525341310004000001000100d15ddcb29688295338af4b7686603fe614abd555e09efba8fb88ee09e1f7b1ccaeed2e8f823fa9eef3fdd60217fc012ea67d2479751a0b8c087a4185541b851bd8b16f8d91b840e51b1cb0ba6fe647997e57429265e85ef62d565db50a69ae1647d54d7bd855e4db3d8a91510e5bcbd0edfbbecaa20a7bd9ae74593daa7b11b4")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] [assembly: Azure.Core.AzureResourceProviderNamespace("Microsoft.CognitiveServices")] diff --git a/sdk/personalizer/Azure.AI.Personalizer/tests/Infrastructure/PersonalizerTestBase.cs b/sdk/personalizer/Azure.AI.Personalizer/tests/Infrastructure/PersonalizerTestBase.cs index 254a2837bd38..2f197a9a45a4 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/tests/Infrastructure/PersonalizerTestBase.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/tests/Infrastructure/PersonalizerTestBase.cs @@ -15,7 +15,7 @@ public abstract class PersonalizerTestBase : RecordedTestBase ranking; private readonly long actionIndex; @@ -40,7 +40,7 @@ private class SlotRankingWrapperForTest : SlotRankingResponseWrapper public override string SlotId { get { return this.slotId; } } - public SlotRankingWrapperForTest(long actionIndex, string slotId, IEnumerable ranked) : base() + internal SlotRankingWrapperForTest(long actionIndex, string slotId, IEnumerable ranked) : base() { this.actionIndex = actionIndex; this.slotId = slotId; @@ -53,7 +53,7 @@ public override IEnumerator GetEnumerator() } } - private class RankingResponseWrapperForTest : RankingResponseWrapper + internal class RankingResponseWrapperForTest : RankingResponseWrapper { private IEnumerable rank; @@ -68,16 +68,16 @@ public override IEnumerator GetEnumerator() } } - private class MultiSlotResponseWrapperForTest : MultiSlotResponseDetailedWrapper + internal class MultiSlotResponseWrapperForTest : MultiSlotResponseDetailedWrapper { - private IEnumerable slotRank; + private IEnumerable slotRank; - public MultiSlotResponseWrapperForTest(IEnumerable rankedSlot) : base() + public MultiSlotResponseWrapperForTest(IEnumerable rankedSlot) : base() { slotRank = rankedSlot; } - public override IEnumerator GetEnumerator() + public override IEnumerator GetEnumerator() { return slotRank.GetEnumerator(); } @@ -100,7 +100,7 @@ protected async Task GetPersonalizerClientAsync(bool isSingl await EnableMultiSlot(adminClient); } var credential = new AzureKeyCredential(apiKey); - var options = InstrumentClientOptions(new PersonalizerClientOptions()); + var options = InstrumentClientOptions(new PersonalizerClientOptions(isLocalInference: isLocalInference, subsampleRate: subsampleRate)); PersonalizerClient personalizerClient = null; if (isLocalInference) { @@ -112,7 +112,7 @@ protected async Task GetPersonalizerClientAsync(bool isSingl } else { - personalizerClient = new PersonalizerClient(new Uri(endpoint), credential, true, options: options, subsampleRate: subsampleRate); + personalizerClient = new PersonalizerClient(new Uri(endpoint), credential, options: options); } } else @@ -148,7 +148,7 @@ private async Task EnableMultiSlot(PersonalizerAdministrationClient adminClient) private RlNetProcessor SetupRlNetProcessor() { - Mock mockLiveModel = new Mock(); + Mock mockLiveModel = new Mock(); List actionProbability = new List { @@ -159,7 +159,7 @@ private RlNetProcessor SetupRlNetProcessor() mockLiveModel.Setup(m => m.ChooseRank(It.IsAny(), It.IsAny(), ActionFlags.Default)).Returns(responseWrapper); Dictionary> slotRankedActions = GetSlotActionProbabilityList(); - List rankedSlots = new List(); + List rankedSlots = new List(); foreach (var item in slotRankedActions) { diff --git a/sdk/personalizer/Azure.AI.Personalizer/tests/Personalizer/DecisionContextTests.cs b/sdk/personalizer/Azure.AI.Personalizer/tests/Personalizer/DecisionContextTests.cs index 67d5a45a6c2c..3b7f67017837 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/tests/Personalizer/DecisionContextTests.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/tests/Personalizer/DecisionContextTests.cs @@ -25,9 +25,9 @@ public void DecisionContextConstructorTest() Assert.AreEqual(decisionContext.ContextFeatures.Count, 1); Assert.IsTrue(decisionContext.ContextFeatures[0].Equals("{\"Features\":{\"day\":\"Monday\",\"time\":\"morning\",\"weather\":\"sunny\"}}")); Assert.AreEqual(decisionContext.Documents.Count, 1); - Assert.AreEqual(decisionContext.Documents[0].ActionFeatureJsonList.Count, 2); - Assert.IsTrue(decisionContext.Documents[0].ActionFeatureJsonList[0].Equals("{\"videoType\":\"documentary\",\"videoLength\":35,\"director\":\"CarlSagan\"}")); - Assert.IsTrue(decisionContext.Documents[0].ActionFeatureJsonList[1].Equals("{\"mostWatchedByAge\":\"30-35\"}")); + Assert.AreEqual(decisionContext.Documents[0].ActionFeatures.Count, 2); + Assert.IsTrue(decisionContext.Documents[0].ActionFeatures[0].Equals("{\"videoType\":\"documentary\",\"videoLength\":35,\"director\":\"CarlSagan\"}")); + Assert.IsTrue(decisionContext.Documents[0].ActionFeatures[1].Equals("{\"mostWatchedByAge\":\"30-35\"}")); } } } diff --git a/sdk/personalizer/Azure.AI.Personalizer/tests/Personalizer/PersonalizerClientForTest.cs b/sdk/personalizer/Azure.AI.Personalizer/tests/Personalizer/PersonalizerClientForTest.cs index b551f79c358d..4bf337e80603 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/tests/Personalizer/PersonalizerClientForTest.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/tests/Personalizer/PersonalizerClientForTest.cs @@ -12,7 +12,7 @@ internal class PersonalizerClientForTest : PersonalizerClient private RlNetProcessor rlNetProcessor; public PersonalizerClientForTest(Uri endpoint, AzureKeyCredential credential, bool isLocalInference, RlNetProcessor rlNetProcessor, float subsampleRate = 1.0f, PersonalizerClientOptions options = null) : - base(endpoint, credential, isLocalInference, subsampleRate, options) + base(endpoint, credential, options) { this.rlNetProcessor = rlNetProcessor; } diff --git a/sdk/personalizer/Azure.AI.Personalizer/tests/Personalizer/RlObjectConverterTests.cs b/sdk/personalizer/Azure.AI.Personalizer/tests/Personalizer/RlObjectConverterTests.cs index b51b62c94194..4ea12a539a24 100644 --- a/sdk/personalizer/Azure.AI.Personalizer/tests/Personalizer/RlObjectConverterTests.cs +++ b/sdk/personalizer/Azure.AI.Personalizer/tests/Personalizer/RlObjectConverterTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using NUnit.Framework; +using static Azure.AI.Personalizer.Tests.PersonalizerTestBase; namespace Azure.AI.Personalizer.Tests { @@ -86,5 +87,119 @@ public void GetActionIdToIndexMappingTest() Assert.AreEqual(idToIndex["SportsArticle"], 1); Assert.AreEqual(idToIndex["EntertainmentArticle"], 2); } + + [Test] + public void GenerateRankResultTest() + { + List originalActions = GetActions(); + List rankableActions = new List(); + List excludedActions = new List(); + rankableActions.Add(originalActions[1]); + rankableActions.Add(originalActions[2]); + rankableActions.Add(originalActions[3]); + excludedActions.Add(originalActions[0]); + + List rankedActions = new List + { + new ActionProbabilityWrapperForTest(0, 0.7f), + new ActionProbabilityWrapperForTest(1, 0.2f), + new ActionProbabilityWrapperForTest(2, 0.1f) + }; + + RankingResponseWrapper responseWrapper = new RankingResponseWrapperForTest(rankedActions); + + string eventId = "testEventId"; + + PersonalizerRankResult rankResponse = RlObjectConverter.GenerateRankResult(originalActions, rankableActions, excludedActions, responseWrapper, eventId); + Assert.AreEqual("action1", rankResponse.RewardActionId); + Assert.AreEqual(originalActions.Count, rankResponse.Ranking.Count); + for (int i = 0; i < rankResponse.Ranking.Count; i++) + { + Assert.AreEqual(originalActions[i].Id, rankResponse.Ranking[i].Id); + } + } + + [Test] + public void GenerateMultiSlotRankResponseTest() + { + string eventId = "testEventId"; + List actions = GetActions(); + + List rankedActionsSlot1 = new List + { + new ActionProbabilityWrapperForTest(1, 0.8f), + new ActionProbabilityWrapperForTest(2, 0.1f), + new ActionProbabilityWrapperForTest(0, 0.1f) + }; + + List rankedActionsSlot2 = new List + { + new ActionProbabilityWrapperForTest(2, 0.9f), + new ActionProbabilityWrapperForTest(0, 0.1f) + }; + + List rankedActionsSlot3 = new List + { + new ActionProbabilityWrapperForTest(0, 1.0f) + }; + + // setup response + List rankedSlots = new List + { + new SlotRankingWrapperForTest(1, "slot1", rankedActionsSlot1), + new SlotRankingWrapperForTest(2, "slot2", rankedActionsSlot2), + new SlotRankingWrapperForTest(0, "slot3", rankedActionsSlot3) + }; + + MultiSlotResponseDetailedWrapper multiSlotResponseWrapper = new MultiSlotResponseWrapperForTest(rankedSlots); + + PersonalizerMultiSlotRankResult response = RlObjectConverter.GenerateMultiSlotRankResponse(actions, multiSlotResponseWrapper, eventId); + + int actionCount = rankedSlots.Count; + Assert.AreEqual(actionCount, response.Slots.Count); + for (int i = 0; i < actionCount; i++) + { + // Assert indices were assigned correctly + var rankedAction = actions[(int)rankedSlots[i].ChosenAction]; + Assert.AreEqual(rankedAction.Id, response.Slots[i].RewardActionId); + } + + Assert.AreEqual(eventId, response.EventId); + } + + private List GetActions() + { + var action0 = new PersonalizerRankableAction( + id: "action0", + features: + new List() { new { SiteId = "testId0" } } + ); + action0.Index = 0; + var action1 = new PersonalizerRankableAction( + id: "action1", + features: + new List() { new { SiteId = "testId1" } } + ); + action1.Index = 1; + var action2 = new PersonalizerRankableAction( + id: "action2", + features: + new List() { new { SiteId = "testId2" } } + ); + action2.Index = 2; + var action3 = new PersonalizerRankableAction( + id: "action3", + features: + new List() { new { SiteId = "testId3" } } + ); + action3.Index = 3; + List actions = new List(); + actions.Add(action0); + actions.Add(action1); + actions.Add(action2); + actions.Add(action3); + + return actions; + } } }