OpenAI .NET C# Web API Available on GitHub
February 12, 2023
• 1,139 views
This video is the third of a multi-part video series where I explore working with OpenAI's web APIs! This is all done in C# and #dotnet.
Join me as I try to explore OpenAI's web API in C#! This entire video is me experimenting and given that I am very new to many concepts in AI, I make many incorrect assumptions. The purpose of this video is:
- Going over the public availability of the OpenAI C# libraries on GitHub.
Enjoying the OpenAI content? You can watch more here:
https://www.youtube.com/...
View Transcript
hey thanks for checking out this video I'm going to be talking about open AI a little bit uh it's been obviously a pretty hot topic um and I've been kind of jumping into a little bit more of the programming side for accessing some of the open Ai apis and more recently um when I was experimenting with a little bit of the fine-tuning which uh I guess I didn't understand enough of and I still don't it's uh it's certainly over my head um I got to play with some of the open AI API and c and um because I use c a lot and I'm hoping that I'll be able to leverage open AI more for different projects and things going forward I figured it'd be a good opportunity to whip up an open- source project for this um just to be able to have a
nice clean API in C to open AI API that's definitely a mouthful to say but um anyway um I had experimented with a little program like I mentioned and then as I was going going through and and actually having some success with the API calls um I really started to see like I should probably start splitting this out into uh more maintainable code and as I was doing it that's where I came up with the idea like why not just publish this as open source it's um I mean it's it's nothing uh like some secret sauce or anything like that I enjoy writing um you know what I consider clean code and I figure this might be a good opportunity to kind of share that with people as well so um I'm going to go ahead and get my screen reoriented here awesome so on
this page here is my GitHub page uh where I have uploaded um to Nexus labs open Ai and this is sort of the just I guess like it's not technically the initial commit there's two cuz the first one was like the the the read me kind of thing that GitHub put in here um but here's basically the layout that we have going on I'll zoom in a little bit so you can see um a little more clearly and we have obviously the solution sort of at the top level then I broken it out into a couple different folders for things so the I'll start with uh just explaining the high level organization here and then I kind of go into a little bit more detail on each of them so um the open AI uh folder here is really the core of um what I
started to do and this is me just wrapping the open AI web apis in C calls um so that you have uh what I feel is like a nice um you know strongly typed uh interface to be able to access all of that stuff and then I also have this uh autofac uh sort of uh extra folder here because I like using autofac for dependency injection if more people wanted to contribute to this over time and stuff and people like using other things instead of autofac I can't see why we couldn't just have other um you know projects and stuff for that as well but I just wanted to call out that I split it out because a lot of the time in my own personal projects I just embed the autofax stuff because I use it for everything but I figured not everyone uses
it so you're not forced to use it if you don't want to and I'll jump into these in just a sec as well but the other folders are sort of uh I have two um functional testing projects um I haven't gotten around to doing uh unit test yet I think that I've structured the code in a way that I could go chip away at writing unit test for you know mocking out the actual web calls and stuff like that and then this examples um folder is actually it has a project in it right now but it's empty but I do want to try to build up a little uh list of examples how you can use the API and then um you know over time people could contribute to that as well with more useful examples for more creative things you could do so that's
the super high Lev part I'm going to jump into the open AI folder here um really what I've done is I've broken out uh a lot of the API access into the same organization that um open AI has done so when you're accessing their API they have a lot of their routes ma by like completion and files and fine tunes and models there's a few more that I haven't finished and and to be quite clear like all of these aren't actually uh fully complete yet either so that's still to come um but the way that this would look is that I'm just going to jump into this interface here which is sort of the primary access point that you would be using when uh dealing with this library and you can see that I have this interface here that essentially has um what maps to
like the different routes or how open AI has laid out their documentation so um I I did it this way because I found that they're documentation it just to me I mean it's not uh I don't know the most like super in-depth and it's not like there's a million different apis that you can call but it just seemed intuitive to be able to organize it this way uh so I figured I'd kind of just stick with that so that way when you go to leverage this you'd be saying like I'm going to um say have like a client like sorry an an iopen AI API client this is going to continue to be a mouthful to say but I'll have instance of that may be just called client so I can say client. files and then call something like upload file on that so that's
how that code would generally read um what I've done to try and minimize people having to like as a consumer of this to have to minimize um I don't know painful instantiation of like trying to know uh how to get all this stuff created is really I just have a factory and the idea in terms of configurability if I can call it that is that I I figured maybe people want to maybe have more fine-tune access with the HTTP client that I'm going to be kind of using for all of my my requests so if you need to do different things for proxying or other stuff sort of in the middle there then um you can actually override the HTTP client so um I I've done it at sort of two different levels um of fraction here so I just wanted to take a brief
moment moment can't speak brief moment to explain that um because you might say well why the heck do you have like a what seems like a handful of classes and interfaces that do the same thing they're slightly different um in order to be able to unit test stuff properly I don't like using some of the built-in. net classes like HTTP client because when you go to call those things with something like mock mq as a framework uh because the not backed by interfaces I just don't generally like having to mess around with trying to not actually make web calls and still kind of simulate that so I wanted to be able to wrap the HTTP client but the HTTP client is also something that I want people to be able to configure if they want to they don't need to if uh they just want
to use it out of the box so I have uh sort of this level of Separation where this IH http client is an interface um that essentially is used by the classes internal to this library and then that way like right now as you can see it only really has this um this single send async method on it so you can send uh HTTP requests and get responses so and have a cancellation token so that's like that's all I need internally right now for the the apis I'm calling from open AI um but if you want to um you know come up with your own um HTTP client Factory so I'm not going into a rapper Factory I'm going into a client Factory right you would take a configuration in which just right now only has a an API key and you give back an
instance of an HTTP client however you want um so you can have granular control here and this is what I would recommend if you wanted to jump in and uh kind of say like whatever I configured as the default HTTP client like you do whatever you want give back one but because I'm going to end up wrapping that right because I end up wrapping that um with this API here with this create um that's a poor example to look at on the interface let me show you the um implementation right the implementation of the wrapper Factory literally will just end up saying go make me an HTTP client so this would be calling either the default one or if you wanted to add in your own Factory we can get an HTTP client out and then I'm just going to wrap it because this wrapper
I think I can just click this and it will cool um yeah so this HTTP client wrapper will just wrap any HTTP client so what I would recommend if possible uh maybe to avoid um you know conflicts down the road or like uh API breakage is almost like treat this IH HTTP client is um the thing that you probably don't want to touch um because for example if some of the internal classes that I have created they start to need say more than just send async then you're going to be in a position where your implementation of IH HTTP client also has to get updated I would like ideally for this to be something that can go tweak um from the almost like the library perspective and not affect users so if people go implementing their own here they can but I I feel like
it will be a spot for breakage so ideally I know I've probably repeated myself a handful of times here but ideally the thing that you're um sort of overriding if you need to is you would have your own implement ation of this class here all right so um inherit from this interface and really all that I'm doing is putting a default header for authorization rate onto the client that's all I'm using it for but you could maybe do proxying stuff and other things in here uh so you can have uh your own implementation so that's all a lot on the HTTP client part but I figure that's the most customizable part really um but the of the stuff like for example I'll just pick one of the the various routes that we could look at so for completions like you would not need oh I
actually left this one as public I'll need to change that so all good example as we're going through this but you would probably not want to be um instantiating your own completions API client because there's not really a need to I'm trying to make it such that you can just use the open AI um API client and then access completions directly off of that so you don't need to go finagle setting all these different individual things up just ask the factory to make you the parent one and you'll get the rest of everything for free okay so I'll have to make a note to come back and actually change that guy to uh to be internal there um and all the the interfaces are are public right so you can use that API but you shouldn't ever need to use the individual classes the exception
um just to jump around again is really on some of these factories like because I'm not forcing anyone to use autofac um some of these factories and their sort of chain of dependencies will be left public so that if you want to you know um explicitly write out a few lines of code to create the factories you can absolutely do that um and if you want to use autofac or another um dependency injection framework then you're you're able to kind of go create that on your own cool okay so that's mostly this part um I'm going to quickly jump into the autofac part as well um just a single module and it should be pretty straightforward um but I had just mentioned sort of leaving a few things as public so that you can um instantiate sort of the the few handful of factor um
few handful that's weird few or handful I don't know um the couple of factories that you'll need to actually get your client created and uh because this one down here this eye openen AI API client Factory is uh sort call it almost like the entry point that's the thing you're going to want to call to create your your client instance um that implementation gets registered and then these other dependencies these other factories feed into it you'll note that I have left um this such that um these are all Singleton because you only need a single instance of them and you will not have to worry you shouldn't have to worry uh because I have these lines in here if you want to override these and have a module that does something else instead then um you can kind of do that so hopefully that um
pattern should work well for folks um and if you don't like autofac and you want to use something else I encourage you um let's get a PLL request in and I don't I don't even know the other uh dependency injection Frameworks because I've been so uh stuck on autofac because I love it but anyway um let's go back quickly on test um I have mostly just a on the autoa side of things I just wanted to make sure that my um some of my resolutions were working as I might expect so um If This Were unit test I'd probably spend a lot more time talking about all the different nuances because I love unit testing but on the functional side I just kept it really really simple um I know someone watching this is about to have a um I don't know a big issue
with how some of these tests look and I'll explain as I get there but I have a couple that just check to make sure that some things I don't expect to be registered aren't so for example the open AI API configuration um like this class is something or might be a record it might it's something that you will have to instantiate on your own um this should not be registered as part of the default container ever because you need to pass in an API key to it so um it should never be registered here and uh similarly if uh if you're looking for the instance of an open AI API client there should never be an instance of that registered the factory should be registered but not an instance of the client so you'll want to resolve the factory so that you can create your
own client instances so hopefully that makes sense but I just wanted to had a have a couple of quick tests in here just to like sanity check these things this is the one that probably someone saw come on screen and they've been waiting for me to stop talking because they're like I wish I could yell at this guy you'll know there's no assert in this test it's okay um I just want to prove that I can resolve this I think literally by default um and I could I could add it in just for uh verbosity but when you resolve an interface from autofac it can't possibly be null it would throw um so like you could I don't even know if uh xunit has aert does not throw anymore but um I could maybe do something like aert not null on result and whatever like
the point is that if this throws an exception it's going to blow up the test and fail it so the fact if we ever can complete this it means that it succeeded so it's a little bit implicit um if people want to fight with me in the comments uh Sweet let's have a discussion but I'm just keeping it simple for now um a couple more ideas here so as I mentioned we want to be able to uh we want to ensure the fact Factory is one that we can resolve but um I'm just doing a couple of quick tests for really simple things like I mentioned that I want these factories to essentially be single instance there's no need to have them any other way um so I'm just basically proving that my uh container setup is as I expect and getting the same instance
the rest of the stuff in here is basically just a a repeat of that so I'll save us all sometime and and skip that um I'm probably just going to show one more spot and then the examples which are empty but uh just talk about it a little bit more but the um I have a couple of tests on the factory uh from the autofact perspective and the reason I did it this way is I have some experience in some of my other projects where I like kind of doing tests around configuration and the idea is that well in in someone's like sort of production app they're probably going to have a container that gets built for autofac if you're using autofac and you would resolve you know um these interfaces to be able to use them so my Approach here is really like I
I want to kind of simulate something that would feel real and there's a particular example that I wanted to look at especially when I was dealing with some disposable patterns and changing my mind back and forth but essentially if I get a client Factory and resolve it and this is the the built-in one that should come out of the package that I've created if I use that factory and ask it to make an instance and then ask it to make an instance again I want to prove that that factory is actually giving me back unique instances so it's just a it's a simple test um it might seem like a bit of an edge case to some people but really I wanted to make sure that the um the instances coming back were unique and I wanted to do it in a way um I
would probably literally I would probably repeat a similar test like this um sort of in the other project that is uh not part of autofact but the more core Library so um I just wanted to prove it sort of with the autofact pattern as well for resolving the default thing that comes out cool okay um last part on here I just want to jump into the examples like I said it is program that looks like nothing uh thanks to not requiring the top level statements anymore um but I'm I'm thinking that I would like to have some examples here maybe where people could see what um doing some generation is like uh for text and being able to pick the models or tune the parameters and stuff like that um I think that would be really cool um uploading deleting listing files things like that
just so you can see how the API looks um and I also think that's uh I mean for me at least and maybe others if they want to contribute it's a good way to actually when I say test out the API I don't mean for like the the functionality of the API but test it out in terms of like does it feel okay to use um super quick example of this is like some of the methods needed uh a ton of parameters and stuff right and being able to make them optional or um changing the interface itself to take in uh a whole other record um or class like depending on your familiarity with C I'm using records a lot more but taking a whole other record as the bundle of all the parameters that kind of thing so test it out in terms of
usability I think that's where um you know this will become really useful and people can check it out maybe they're looking through the examples and they're like hey like you know um there's I don't know I haven't gone in checked to be honest but but maybe there's a hundred other um open AI C libraries and people are like yeah this one I don't like how the syntax and stuff reads like cool like that's okay um you know use someone else's write your own you know I'm not offended by it uh but I think it will be good practice and I think that's mostly it I just also wanted to mention that the uh I published the the first kind of versions of these packages in on uh nugat um so there's the open AI one and then the autofac package as well and yeah I
think that's probably it I don't have um personal experience with doing some of the stuff um in git specifically for getting packages and stuff built I've dabbled with other CI pipelines and stuff like that but not so much directly in git to have it all wired up and having like the the build Badges and the test Badges and all that kind of stuff so um that might be kind of cool if someone else wants to help out with that if not maybe I'll get to it at some point but um I think that's it I'll put some links in the description so you guys can check this out if you're interested um and yeah thanks for watching if uh if you think this is uh interesting you know like the video that'd be awesome you can you know go Star the the GitHub page um
leave a comment below if you have any thoughts about about how this is organized or if you're you know considering using it or you're using something else already uh and of course you know uh I would appreciate if you subscribe to the channel if you like content like this and it lets me know that you enjoy content like this so thanks for watching and we'll see you next time
Frequently Asked Questions
What is the purpose of the OpenAI .NET C# Web API project?
The purpose of the OpenAI .NET C# Web API project is to provide a clean and maintainable interface for accessing OpenAI's APIs using C#. I wanted to create an open-source project that allows developers to leverage OpenAI's capabilities in their own applications while ensuring the code is well-organized and easy to use.
How can I contribute to the OpenAI .NET C# Web API project?
You can contribute to the OpenAI .NET C# Web API project by checking out the GitHub repository, where you can submit pull requests with improvements, additional features, or examples. I encourage contributions, especially if you have ideas for enhancing the library or if you want to add examples of how to use the API.
What dependencies does the OpenAI .NET C# Web API project use?
The OpenAI .NET C# Web API project uses Autofac for dependency injection, but I designed it in a way that allows flexibility. If you prefer to use a different dependency injection framework, you can do that as well. The project is structured to accommodate various configurations, so you can customize it according to your needs.
These FAQs were generated by AI from the video transcript.