Writing a C# SDK for the OpenAI Gym using .NET Core

When we take a look at the OpenAI Gym on Github (https://github.com/openai/gym-http-api), we see that it does not have bindings available for C#. Now since I am a firm believer of .NET Core and what it brings to developer ecosystem, I decided to write one myself (https://github.com/Xaviergeerinck/dotnetcore-sdk-openai). Using what I learned in my previous blog post How to write a SDK in dotnet Core I created one that looks like this for the main method:

Note: You can test it using the Test() method in this class
using System;
using System.Threading.Tasks;
using System.Net.Http;
using OpenAI.SDK.Models.Response;
using OpenAI.SDK.Models.Request;
using Newtonsoft.Json;
using System.Text;

namespace OpenAI.SDK
{
    public class ApiService
    {
        private static readonly HttpClient client = new HttpClient();

        private readonly string host;

        public ApiService(string host = "http://127.0.0.1:5000")
        {
            this.host = host;
        }

        public async Task Test()
        {
            Console.WriteLine("========== CREATE ENVIRONMENT ==========");
            var env = await EnvCreate("CartPole-v0");
            Console.WriteLine("Created Environment: " + env.InstanceID);

            Console.WriteLine("========== ALL ENVIRONMENTS ==========");
            Console.WriteLine(await EnvListAll());

            Console.WriteLine("========== INITIAL OBSERVATION ==========");
            var initialObservation = await EnvReset<double[]>(env.InstanceID);
            Console.WriteLine(initialObservation.Observation);

            Console.WriteLine("========== STEP ==========");
            var stepTaken = await EnvStep<double[]>(env.InstanceID, 1);
            Console.WriteLine(stepTaken.IsDone);
            Console.WriteLine(stepTaken.Reward);
            Console.WriteLine(stepTaken.Observation);

            Console.WriteLine("========== SPACES ==========");
            var actionSpace = await EnvActionSpaceInfo<dynamic>(env.InstanceID);
            var observationSpace = await EnvObservationSpaceInfo<dynamic>(env.InstanceID);
            Console.WriteLine(actionSpace);
            Console.WriteLine(observationSpace);
        }

        public async Task<EnvGetAllResponse> EnvListAll()
        {
            var json = await client.GetStringAsync($"{this.host}/v1/envs/");
            var resParsed = JsonConvert.DeserializeObject<EnvGetAllResponse>(json);
            return resParsed;
        }

        public async Task<EnvCreateResponse> EnvCreate(string envID)
        {
            var requestBody = new EnvCreateRequest {
                EnvId = envID
            };
            var requestJson = new StringContent(JsonConvert.SerializeObject(requestBody), Encoding.UTF8, "application/json");

            var res = await client.PostAsync($"{this.host}/v1/envs/", requestJson);
            var resContent = await res.Content.ReadAsStringAsync();
            var resParsed = JsonConvert.DeserializeObject<EnvCreateResponse>(resContent);
            return resParsed;
        }

        public async Task<EnvResetResponse<T>> EnvReset<T>(string instanceID)
        {
            var res = await client.PostAsync($"{this.host}/v1/envs/{instanceID}/reset/", null);
            var resContent = await res.Content.ReadAsStringAsync();
            var resParsed = JsonConvert.DeserializeObject<EnvResetResponse<T>>(resContent);
            return resParsed;
        }

        public async Task<EnvStepResponse<T>> EnvStep<T>(string instanceID, int action, bool isRender = false)
        {
            var requestBody = new EnvStepRequest {
                Action = action,
                IsRender = isRender
            };

            var requestJson = new StringContent(JsonConvert.SerializeObject(requestBody), Encoding.UTF8, "application/json");

            var res = await client.PostAsync($"{this.host}/v1/envs/{instanceID}/step/", requestJson);
            var resContent = await res.Content.ReadAsStringAsync();
            var resParsed = JsonConvert.DeserializeObject<EnvStepResponse<T>>(resContent);
            return resParsed;
        }

        public async Task<EnvGetActionSpaceResponse<T>> EnvActionSpaceInfo<T>(string instanceID)
        {
            var json = await client.GetStringAsync($"{this.host}/v1/envs/{instanceID}/action_space/");
            var resParsed = JsonConvert.DeserializeObject<EnvGetActionSpaceResponse<T>>(json);
            return resParsed;
        }

        public async Task<EnvGetObservationSpaceResponse<T>> EnvObservationSpaceInfo<T>(string instanceID)
        {
            var json = await client.GetStringAsync($"{this.host}/v1/envs/{instanceID}/observation_space/");
            var resParsed = JsonConvert.DeserializeObject<EnvGetObservationSpaceResponse<T>>(json);
            return resParsed;
        }

        public async Task<EnvMonitorStartResponse> EnvMonitorStart(string instanceID, string directory, bool force, bool resume)
        {
            var requestBody = new EnvMonitorStartRequest {
                Directory = directory,
                Force = force,
                Resume = resume
            };

            var requestJson = new StringContent(JsonConvert.SerializeObject(requestBody), Encoding.UTF8, "application/json");

            var res = await client.PostAsync($"{this.host}/v1/envs/{instanceID}/monitor/start", requestJson);
            var resContent = await res.Content.ReadAsStringAsync();
            var resParsed = JsonConvert.DeserializeObject<EnvMonitorStartResponse>(resContent);
            return resParsed;
        }

        public async Task<EnvMonitorStopResponse> EnvMonitorStop(string instanceID)
        {
            var res = await client.PostAsync($"{this.host}/v1/envs/{instanceID}/monitor/close", null);
            var resContent = await res.Content.ReadAsStringAsync();
            var resParsed = JsonConvert.DeserializeObject<EnvMonitorStopResponse>(resContent);
            return resParsed;
        }

        public async Task<EnvCloseResponse> EnvClose(string instanceID)
        {
            var res = await client.PostAsync($"{this.host}/v1/envs/{instanceID}/close", null);
            var resContent = await res.Content.ReadAsStringAsync();
            var resParsed = JsonConvert.DeserializeObject<EnvCloseResponse>(resContent);
            return resParsed;
        }

        public async Task<ShutdownServerResponse> ShutdownServer()
        {
            var res = await client.PostAsync($"{this.host}/v1/shutdown", null);
            var resContent = await res.Content.ReadAsStringAsync();
            var resParsed = JsonConvert.DeserializeObject<ShutdownServerResponse>(resContent);
            return resParsed;
        }
    }
}