-
Notifications
You must be signed in to change notification settings - Fork 77
Interact via HTTP
Before you can interact with your Decision Service (DS), you will need the address and password of its HTTP front-end. These correspond to the "Web Service Address" and the "User Token" that are displayed in the summary, after deployment completes. If you picked your own password at the start of deployment, then you will already know it. Otherwise, it will be generated randomly.
If you are already familiar with contextual bandits and HTTP POST, then all you need to know is in the Front-end HTTP API Reference.
Each interaction with the DS has 3 parts:
- Send a context.
- Receive a decision, along with an event ID.
- Send the event ID together with a reward that reflects how good the decision was.
Here is how the DS HTTP API arranges these parts into HTTP messages:
- It expects the 1st and 3rd parts of the interaction as POST requests. It returns the 2nd part as the response to the 1st POST request. The response to the other POST request can be ignored.
- Both POST requests must carry the password in their "auth" header.
- The URL for sending the context is the web service address, appended with '/API/' and the decision type. Our running example in this tutorial uses action-dependent features, with a 'Ranker' decision type. Contexts without action-dependent features can be used with the 'Policy' decision type. See the Front-end HTTP API Reference for more details.
- The body of the 1st POST should be a context encoded in JSON format, as described on https://github.com/JohnLangford/vowpal_wabbit/wiki/JSON.
- The response from the 1st POST is a JSON-encoded string, one of whose fields is "Actions". The value of that field is an array of integers, representing decisions ordered from best to worst.
- The event ID is the value of the response's EventId field. That value is a string.
- The URL for sending the reward is the web service address, appended with '/API/Reward/?eventId=', followed by the event ID.
- The reward itself is sent as a floating-point number in the body of the 2nd POST. Nothing else should be in that body.
We will now walk through a simple but complete example of an interaction. This example uses the WebClient library in C#, but you could accomplish the same thing using many other libraries in many other languages. The complete code is at the bottom of this page. It starts with the following usings:
using System.Net;
using System.Text;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
Both POST requests must carry the password (=web service token) in their header. For example, if your password is stored in the variable myPassword then you can start preparing your POST as follows:
var wc = new WebClient();
wc.Headers.Add("auth", myPassword);
The next piece of information to encode is the URL to which the POST will be sent. For sending the context (1st POST), the web service address must be appended with a '/' followed by the decision type. For the present purposes, the decision type is always 'Ranker'. For example, if your management center url is http://app123-mc-jvj7wdddvsdwe.azurewebsites.net, then you can encode it like this:
readonly string baseUrl = "http://app123-mc-jvj7wdddvsdwe.azurewebsites.net";
readonly string contextType = "Ranker";
string contextUri = string.Format(CultureInfo.InvariantCulture, "{0}/API/{1}", baseUrl, contextType);
Now it's time to encode the context. Here is a JSON encoding of a simple example that uses action-dependent features:
{
"Age": 25,
"Location": "New York",
_multi:
[
{ "Topic": "Politics"},
{ "Topic": "Technology"}
]
}
This example has 2 shared feature types (Age and Location), and one action-dependent feature type (Topic). Note: In the _multi array above, Decision Service will detect 2 actions where "Topic": "Politics" is action 1 and "Topic": "Technology" is action 2 according to their orders in the list. And here is some code to encode this context for a POST.
string contextString = "{ \"Age\": 25, Location: \"New York\", _multi: [{ \"Topic\": "Politics"}, { \"Topic\": "Technology"}]}";
byte[] context = System.Text.Encoding.ASCII.GetBytes(contextString);
As shown above, you must convert the string to a byte array, because that is the parameter type expected by the method that we will now use to send the POST:
var response = wc.UploadData(contextUri, "POST", context);
We are now ready to unpack the response:
var utf8response = UnicodeEncoding.UTF8.GetString(response);
JObject jobj = JObject.Parse(utf8response);
JArray actions = (JArray)jobj["Actions"];
int topAction = (int)actions[0];
Of course, you can also look at other elements of the array if you are interested in the complete ranking rather than just the top action. To function properly, the DS must receieve a reward POST for every context POST. The URL for sending the reward is the web service address, appended with '/Reward/?eventId=', followed by the event ID. You can extract the event ID from the response to the 1st POST and encode the reward URL as follows:
string eventID = (string)jobj["EventId"];
string rewardUri = string.Format(CultureInfo.InvariantCulture, "{0}/API/Reward/?eventId={1}", baseUrl, eventID);
The reward is sent as the body of the 2nd POST. So the 2nd POST can be achieved as follows:
string rewardString = "1";
byte[] reward = System.Text.Encoding.ASCII.GetBytes(rewardString);
response = wc.UploadData(rewardUri, "POST", reward);
As before, don't forget to convert the POST body to a byte array. The response from the 2nd POST should be empty; you can ignore it.
And that's it! For your convenience, here is all of the above code collected together:
using System.Net;
using System.Text;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
// set up
var wc = new WebClient();
wc.Headers.Add("auth", myPassword);
readonly string baseUrl = "http://app123-mc-jvj7wdddvsdwe.azurewebsites.net";
// send context
readonly string contextType = "Ranker";
string contextUri = string.Format(CultureInfo.InvariantCulture, "{0}/API/{1}", baseUrl, contextType);
string contextString = "{ \"Age\": 25, \"Location\": \"New York\", _multi: [{ \"Topic\": "Politics"}, { \"Topic\": "Technology"}]}";
byte[] context = System.Text.Encoding.ASCII.GetBytes(contextString);
var response = wc.UploadData(contextUri, "POST", context);
// decode response (decision + event ID)
var utf8response = UnicodeEncoding.UTF8.GetString(response);
JObject jobj = JObject.Parse(utf8response);
JArray actions = (JArray)jobj["Actions"];
int topAction = (int)actions[0];
string eventID = (string)jobj["EventId"];
// send reward
string rewardUri = string.Format(CultureInfo.InvariantCulture, "{0}/API/Reward/?eventId={1}", baseUrl, eventID);
string rewardString = "1";
byte[] reward = System.Text.Encoding.ASCII.GetBytes(rewardString);
response = wc.UploadData(rewardUri, "POST", reward);
To get the most out of the DS, you need a deeper understanding of how to encode contexts and rewards. Currently, the best summary of this topic is the MWT White Paper.