How-To write a C# SDK in .NET Core
For interacting with OpenAI through C#, I decided to write one myself. But how do we get started writing a SDK?
Getting started writing our SDK
Deciding on the structure of your SDK
First of all we need to decide on how we want to use it. This is a crucial step, since it will notate how easy it is to use, and influence the adoption rate in the long run.
In this case, I decided that the easiest way to start is by being able to create an instance of a class that allows me to set properties such as the host and others (maybe even an access key in the future for online APIs). For the Project structure I decided on creating a solution with a unique namespace that can be re-used and imported by my other solutions.
Configuring the Project structure with dotnet core
Start by creating the solution and sub projects:
# Create 2 Projects, one for the SDK and one for the Consumer
dotnet new classlib --name <sdk>
dotnet new console --name <sdk-consumer>
# Create a solution and add the 2 projects
dotnet new sln --name <name>
dotnet sln <name> add <sdk>
dotnet sln <name> add <sdk-consumer>
# Add a reference to Newonsoft.Json for JSON parsing
dotnet add <sdk> package NewtonSoft.Json
Afterwards we add a reference in our \<sdk-consumer> to our ./<sdk-consumer>/<sdk-consumer>.csproj
file so that it looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\<sdk>\sdk.csproj" />
</ItemGroup>
</Project>
Writing our entry class in the SDK
To be able to access our SDK, we can now create a class in the \<sdk> project:
using System;
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;
}
}
}
That we are then able to use in other class like this:
// Imports
using OpenAI.SDK; // or your own namespace
// The code in your method or constructor:
var sdk = new ApiService("http://127.0.0.1:1337");
sdk.DoCall();
Allowing me to inject it further on and still have the same properties wherever I use it.
Creating API calls from the SDK
Since I will be interacting with a JSON API, there are 2 calls that are mainly used: GET and POST. To be able to perform this call, we create a HttpClient
and set it in our constructor like this:
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;
}
}
}
Performing a GET call
For our GET call we do a simple GetStringAsync
call through our HttpClient
which we will then decode by using the NewtonSoft.JSON
's excellent DeserializeObject
method that uses a POCO class, resulting in:
public async Task<EnvGetAllResponse> EnvListAll()
{
var json = await client.GetStringAsync($"{this.host}/v1/envs/");
var resParsed = JsonConvert.DeserializeObject<EnvGetAllResponse>(json);
return resParsed;
}
POCO Class:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace OpenAI.SDK.Models.Response
{
public class EnvGetAllResponse
{
[JsonProperty("all_envs")]
public Dictionary<string, string> Environments { get; set; }
}
}
Performing a POST call
The POST call almost exactly goes in the same format, except that we will have to pass a body to it.
To do this we create a Request POCO class in its own namespace such as:
using Newtonsoft.Json;
namespace OpenAI.SDK.Models.Request
{
public class EnvCreateRequest
{
[JsonProperty("env_id")]
public string EnvId { get; set; }
}
}
That we can then initialize and send after serializing it with SerializeObject
through the PostAsync
call in our HttpClient
object:
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;
}