Would you like to inspect the original subtitles? These are the user uploaded subtitles that are being translated:
1
00:00:00,000 --> 00:00:15,960
In this episode, we're going to have a look at a blog post system that can accept comments.
2
00:00:15,960 --> 00:00:21,000
And the interesting thing about these comments is that if we enter in one, we can then
3
00:00:21,000 --> 00:00:24,640
create the comment, and then you'll see that we have a button.
4
00:00:24,640 --> 00:00:29,440
In the focus of this episode, it's going to be creating this button that could be hindered,
5
00:00:29,440 --> 00:00:34,160
and also an API wrapper for the OpenAI API.
6
00:00:34,160 --> 00:00:39,120
So when we click the button, it'll make a request, and then that request will be an
7
00:00:39,120 --> 00:00:40,880
AI response.
8
00:00:40,880 --> 00:00:46,320
And so there's a lot of different use cases that we can do with this with OpenAI, and they
9
00:00:46,320 --> 00:00:48,120
have a pretty extensive AI.
10
00:00:48,120 --> 00:00:52,880
So if you just want some kind of text generation, or if you want to have some kind of code
11
00:00:52,880 --> 00:00:59,040
generation, which, GitHub's, Copilot actually uses, Codex under the hood, and there's
12
00:00:59,040 --> 00:01:01,040
many more examples.
13
00:01:01,040 --> 00:01:06,520
And so in this episode, we're going to be using the OpenAI and the Deventy model in order
14
00:01:06,520 --> 00:01:09,360
to have this AI bot response.
15
00:01:09,360 --> 00:01:13,120
And because we are using the API, it is a paid solution.
16
00:01:13,120 --> 00:01:17,560
But if you sign up for a free account, you can get about $18 worth of credit that can
17
00:01:17,560 --> 00:01:20,039
be used within the first three months.
18
00:01:20,039 --> 00:01:25,039
And so the cost associated with this is going to vary depending on the amount of traffic
19
00:01:25,040 --> 00:01:30,560
to your website, as well as how much the actual AI is going to be used.
20
00:01:30,560 --> 00:01:36,120
But as we'll see, we do have some mitigating factors that we can do to limit the usage.
21
00:01:36,120 --> 00:01:41,480
And then we're also going to be creating an API wrapper around OpenAI, because it's one
22
00:01:41,480 --> 00:01:46,640
of those things where we are creating an API request for one external API.
23
00:01:46,640 --> 00:01:50,360
But in our application, there's good chances that there's going to be many more APIs that
24
00:01:50,360 --> 00:01:55,640
we're going to be interacting with, and without bringing in any external libraries, I would
25
00:01:55,640 --> 00:02:00,440
like to just create a simple wrapper that we're then going to be able to reuse within
26
00:02:00,440 --> 00:02:02,160
our application.
27
00:02:02,160 --> 00:02:07,120
And so we are going to start out this application with the blog post already created.
28
00:02:07,120 --> 00:02:09,560
And we're not doing anything fancy with that.
29
00:02:09,560 --> 00:02:12,000
We have a very basic device set up.
30
00:02:12,000 --> 00:02:15,920
A user has many posts and a user has many comments.
31
00:02:15,920 --> 00:02:21,119
And as you would expect, a post has many comments and that belongs to the user.
32
00:02:21,119 --> 00:02:26,359
The post has a rich text content, so we're using the action text.
33
00:02:26,359 --> 00:02:31,320
And for the comments, that belongs to the user and it also belongs to the post.
34
00:02:31,320 --> 00:02:33,920
And we also have the rich text content.
35
00:02:33,920 --> 00:02:37,440
So again, we're using action text for the comments.
36
00:02:37,440 --> 00:02:41,839
We have our controller for the post and the controller for the comments, neither of which
37
00:02:41,839 --> 00:02:44,440
we're really going to have to touch in this episode.
38
00:02:44,440 --> 00:02:48,480
For this episode, I was very intentional to keep it as simple as possible.
39
00:02:48,480 --> 00:02:54,520
So I'm not introducing any kind of hot wire with the turbo frame tags or stimulus controllers
40
00:02:54,520 --> 00:02:55,520
for this.
41
00:02:55,520 --> 00:03:01,640
I wanted to just keep it pretty bare bones, so we can focus on building the AI integration.
42
00:03:01,640 --> 00:03:06,520
And so the first thing that you need to do is go to openAI.com and sign up for a free account.
43
00:03:06,520 --> 00:03:09,440
We then generate some kind of API key.
44
00:03:09,440 --> 00:03:12,040
And you'll just want to copy this and keep it secret.
45
00:03:12,040 --> 00:03:17,799
And so first, let's go into our routes file and we'll make a route for the open AI to comment.
46
00:03:17,799 --> 00:03:21,880
So we'll just create a block under the resources for the comments.
47
00:03:21,880 --> 00:03:27,560
And the reason for that is because when we make the call to open AI, it is going to generate
48
00:03:27,560 --> 00:03:31,680
a response based on the prompt we give it and the prompt that we're going to give it is
49
00:03:31,680 --> 00:03:32,920
the comment.
50
00:03:32,920 --> 00:03:37,440
So then we could also have a resources for the comments.
51
00:03:37,440 --> 00:03:39,799
And we would only need the create action.
52
00:03:39,800 --> 00:03:43,240
But I don't like doing this because this is very confusing.
53
00:03:43,240 --> 00:03:48,240
If I were to just look at this, I would think that this is so that people can make a comment
54
00:03:48,240 --> 00:03:52,680
on a comment and it has nothing to do with the open AI.
55
00:03:52,680 --> 00:03:57,480
So instead, what I would rather do is to just wrap this comment that we just created
56
00:03:57,480 --> 00:03:59,040
in a name space.
57
00:03:59,040 --> 00:04:01,360
And we'll just call this the open AI.
58
00:04:01,360 --> 00:04:04,240
We'll create our block and they will close it out.
59
00:04:04,240 --> 00:04:07,920
And before we move on from that, let's go ahead and run the Rails routes.
60
00:04:07,920 --> 00:04:13,320
Dash G. So we can see what the routes are and we'll grab the open AI.
61
00:04:13,320 --> 00:04:15,359
And so we can see our prefix.
62
00:04:15,359 --> 00:04:18,440
This is going to have the URI for the posts.
63
00:04:18,440 --> 00:04:22,200
So we'll pass in the post ID, the comments, the common ID.
64
00:04:22,200 --> 00:04:27,200
And then it'll just go to the fourth slash open AI, fourth slash comments.
65
00:04:27,200 --> 00:04:33,960
And then we have the open AI name space, a comments controller with the create action.
66
00:04:33,960 --> 00:04:36,159
And so I'm happy enough with this.
67
00:04:36,160 --> 00:04:42,000
We then generate our controller and we'll just call it the open AI, fourth slash comments.
68
00:04:42,000 --> 00:04:47,720
And as we would expect, that would create the folder open AI and the comments controller.
69
00:04:47,720 --> 00:04:51,000
And within here, we can make our create action.
70
00:04:51,000 --> 00:04:56,240
Similar to what we are doing in other areas of the application, we have a before action
71
00:04:56,240 --> 00:04:58,080
for the authenticate user.
72
00:04:58,080 --> 00:05:02,680
And that simply a device helper that we want to make sure that on the open AI that we do
73
00:05:02,680 --> 00:05:07,960
have a user authenticated before we make that API call to open AI.
74
00:05:07,960 --> 00:05:13,000
And so I'm going to lead this alone for now because what we are displaying out the comments,
75
00:05:13,000 --> 00:05:15,680
we can trace that back if we look at our posts.
76
00:05:15,680 --> 00:05:20,240
When we are showing a post, you'll see that we are rendering out the post comments.
77
00:05:20,240 --> 00:05:24,040
And so we need to look at the comments folder in the comment partial.
78
00:05:24,040 --> 00:05:30,360
And then here we have where we are displaying the user, said on the date, and then the content.
79
00:05:30,360 --> 00:05:32,040
So let's go ahead and create our link.
80
00:05:32,040 --> 00:05:36,800
I'm not going to do a link too because we do have a post request that we are making for
81
00:05:36,800 --> 00:05:38,400
that create action.
82
00:05:38,400 --> 00:05:41,000
So instead, I'll do a button too.
83
00:05:41,000 --> 00:05:42,400
We can then give it some text.
84
00:05:42,400 --> 00:05:45,800
We'll just call it the ask AI to respond.
85
00:05:45,800 --> 00:05:51,960
And then this needs to go to the post comment, open AI comments path.
86
00:05:51,960 --> 00:05:58,040
We do need to pass in both our posts and the comment for this particular endpoint.
87
00:05:58,040 --> 00:06:02,320
I'm also going to have a class because I am using bootstrap on this project.
88
00:06:02,320 --> 00:06:06,920
I'll just make a button small and then a button outline primary.
89
00:06:06,920 --> 00:06:12,040
And just so you know, this post and the comment, we do have access to both of these
90
00:06:12,040 --> 00:06:19,320
because when we are displaying out in the post show page, we're rendering out the post comments.
91
00:06:19,320 --> 00:06:23,280
So this will automatically create that local variable comment.
92
00:06:23,280 --> 00:06:28,679
But then we are passing in the local variable post sending it to the instance variable.
93
00:06:28,679 --> 00:06:32,719
Because when I'm working in partials, I don't like use it instance variables within
94
00:06:32,719 --> 00:06:33,719
here.
95
00:06:33,719 --> 00:06:37,960
For a few reasons, it may not be clear what that object should actually be.
96
00:06:37,960 --> 00:06:43,200
And if that instance variable was even required by using local variables instead of the instance
97
00:06:43,200 --> 00:06:48,520
variables, then we would actually receive an error that the variable wasn't defined.
98
00:06:48,520 --> 00:06:53,159
Otherwise we would get a nil and then we could get into situations where we get a
99
00:06:53,159 --> 00:06:58,359
undefined method created at four nil class and we don't want to deal with those kind
100
00:06:58,359 --> 00:06:59,359
of issues.
101
00:06:59,359 --> 00:07:03,640
And so surprisingly, this is really all we have to do on our front end.
102
00:07:03,640 --> 00:07:07,280
We created the endpoint and we created our button.
103
00:07:07,280 --> 00:07:09,880
Everything else is going to be done on the back end.
104
00:07:09,880 --> 00:07:14,960
And we'll start with then the controllers, open AI and the comments controller.
105
00:07:14,960 --> 00:07:19,120
Because there's a lot of different ways that we can architect this, but essentially what
106
00:07:19,120 --> 00:07:22,599
I want to do is that we're going to create a new comment.
107
00:07:22,599 --> 00:07:27,560
And that comment is going to be from our current user, which we do have access to, especially
108
00:07:27,560 --> 00:07:31,919
because we are requiring that user to be authenticated and then we'll have a comment
109
00:07:31,919 --> 00:07:32,919
start new.
110
00:07:32,919 --> 00:07:38,000
We can set that new comment and we can set the post is equal to the post, which that
111
00:07:38,000 --> 00:07:39,840
post we don't have access to.
112
00:07:39,840 --> 00:07:44,880
So we're going to create a private method for our post. We'll send an instance variable post
113
00:07:44,880 --> 00:07:49,840
double pipe equals just so we are memoizing it because we are going to refer to the post
114
00:07:49,840 --> 00:07:54,640
potentially a few different times and then we can have a post stop find and then we need
115
00:07:54,640 --> 00:08:00,400
to pass in the prams and then the post underscore ID and that post underscore ID.
116
00:08:00,400 --> 00:08:05,840
If you remember, if we look at our routes, we have our post and then that post ID and then
117
00:08:05,840 --> 00:08:10,719
for the comments, we have the comment ID and we're also going to need that comment because
118
00:08:10,719 --> 00:08:13,560
we need to get the content of that comment.
119
00:08:13,560 --> 00:08:17,520
And so we're not going to be using the comment multiple times, so there's no need to
120
00:08:17,520 --> 00:08:21,239
memoize it, but we are going to call on our post.
121
00:08:21,239 --> 00:08:25,359
Notice that I'm just using the local variable and not the instance variable that we
122
00:08:25,359 --> 00:08:29,400
said because we don't know if that instance variable has been set yet.
123
00:08:29,400 --> 00:08:31,280
We name call the comments.
124
00:08:31,280 --> 00:08:36,880
Find and we can find the comment based on the prams comment underscore ID.
125
00:08:36,880 --> 00:08:41,679
So now that we have created a new comment, we haven't saved the yet and we've also
126
00:08:41,679 --> 00:08:44,280
associated the comment to a post.
127
00:08:44,280 --> 00:08:49,640
We then set some kind of content so we have our new comment dot content and we want to
128
00:08:49,640 --> 00:08:55,040
set this equal to and we'll propend some text on here, we'll just call the AI said and
129
00:08:55,040 --> 00:08:58,280
then we can call some kind of external service.
130
00:08:58,280 --> 00:09:03,319
So we wouldn't want to put all of that logic or call an open AI within our controller.
131
00:09:03,319 --> 00:09:06,319
So I'm going to create a different kind of class.
132
00:09:06,319 --> 00:09:09,400
We'll just call this the open AI commenter.
133
00:09:09,400 --> 00:09:14,680
We'll have a class method called call and then we're just going to pass in the comment
134
00:09:14,680 --> 00:09:20,439
dot content, but the problem with this is that we just need the plain text of the content.
135
00:09:20,439 --> 00:09:27,480
And because this content is action text, we can call to underscore plain underscore text.
136
00:09:27,480 --> 00:09:33,720
We then check if the new comment that save, we can then just redirect to the post with
137
00:09:33,720 --> 00:09:38,720
the notice AI has responded and that's all we're going to do in here for now.
138
00:09:38,720 --> 00:09:44,120
If you do have your system set up for hot wire where you're broadcasting the changes
139
00:09:44,120 --> 00:09:48,840
that you would need to redirect to the post, you could just call the new comment dot save
140
00:09:48,840 --> 00:09:50,840
then maybe a head okay.
141
00:09:50,840 --> 00:09:56,200
So that way it's not expecting any kind of return value or input, but then turbo would
142
00:09:56,200 --> 00:10:00,280
then broadcast the response stream to the browsers.
143
00:10:00,280 --> 00:10:05,720
And I would definitely prefer doing that over just calling our class here because then we could
144
00:10:05,720 --> 00:10:08,120
just call this from a background job.
145
00:10:08,120 --> 00:10:13,960
So that way the end user isn't having to wait for open AI to generate a response.
146
00:10:13,960 --> 00:10:18,280
But again, those kind of implementations are very simple and we've already covered them
147
00:10:18,280 --> 00:10:23,480
in the past another episodes because we have done the turbo stream broadcasts and we've
148
00:10:23,480 --> 00:10:25,560
done plenty on background jobs.
149
00:10:25,560 --> 00:10:30,839
You would simply just move this into a background job passing in the comment and the broadcast
150
00:10:30,839 --> 00:10:34,119
within the comments model would handle the rest.
151
00:10:34,119 --> 00:10:38,680
So I'm going to undo these changes for now because we're not going to set that part up.
152
00:10:38,680 --> 00:10:41,319
We're just going to redirect back to the post.
153
00:10:41,319 --> 00:10:47,160
So we do need to create this open AI commenter and I'm just going to create a new file in the
154
00:10:47,160 --> 00:10:48,119
models.
155
00:10:48,119 --> 00:10:51,479
We'll call it the open AI commenter dot RB.
156
00:10:51,480 --> 00:10:58,120
It'll be a class of open AI commenter will have a class method named the call method.
157
00:10:58,120 --> 00:11:02,040
And we're going to be taking the comments content plain text.
158
00:11:02,040 --> 00:11:03,880
So I'm just going to call it the prompt.
159
00:11:03,880 --> 00:11:09,640
We'll create a new instance of this class passing in the prompt and then we'll have a call method.
160
00:11:09,640 --> 00:11:12,440
We can initialize a class taking in our prompt.
161
00:11:12,440 --> 00:11:17,640
Well, then set our instance variable prompt is equal to the prompt and then we'll have the call
162
00:11:17,640 --> 00:11:20,760
method where we'll do all the business logic.
163
00:11:20,760 --> 00:11:23,560
And so we do have a few different things going on here.
164
00:11:23,560 --> 00:11:29,319
I'm first going to set a constant and that constant I'm just going to put the URL for the open
165
00:11:29,319 --> 00:11:32,360
AI's completion API.
166
00:11:32,360 --> 00:11:38,520
And while I could do all of the necessary bit of code to make the API call from here, I actually
167
00:11:38,520 --> 00:11:39,640
don't want to do that.
168
00:11:39,640 --> 00:11:46,439
I want this open AI commenter to be specifically on the formation of the request and getting the
169
00:11:46,440 --> 00:11:47,560
response.
170
00:11:47,560 --> 00:11:53,800
I want to create an API wrapper that's then going to make whatever kind of API requests we want
171
00:11:53,800 --> 00:11:58,520
to make, whether it's a get request or a post or a putter patch.
172
00:11:58,520 --> 00:12:02,520
And so this API let's just call it an API client.
173
00:12:02,520 --> 00:12:08,920
And this API client, we're going to initialize it and we're going to take in a few different variables.
174
00:12:08,920 --> 00:12:11,080
We're going to take in our URL.
175
00:12:11,080 --> 00:12:16,120
We also are going to create some kind of headers and then we're also going to have
176
00:12:16,120 --> 00:12:18,840
some kind of body that we're going to pass in.
177
00:12:18,840 --> 00:12:27,080
We then have an action where do we want to do a get request, a put request, a delete, or in our case,
178
00:12:27,080 --> 00:12:29,880
we want to make a post request to this endpoint.
179
00:12:29,880 --> 00:12:31,960
We can set this to a local variable.
180
00:12:31,960 --> 00:12:38,360
And for open AI specifically, it's going to give us a response back, which we'll look at.
181
00:12:38,360 --> 00:12:41,080
And we can actually do that with a Rails.logger.
182
00:12:41,080 --> 00:12:45,800
And unlike using emojis in my logs, just so I can see it more clearly.
183
00:12:45,800 --> 00:12:50,839
And we'll just spit out that JSON because I've already done this in preparations.
184
00:12:50,839 --> 00:12:54,439
I know that this is going to return some kind of choices.
185
00:12:54,439 --> 00:12:58,520
And then we can get the first item and then the text response.
186
00:12:58,520 --> 00:13:04,280
If the choices is nil, because it will some kind of issue, maybe we've hit some kind of limits,
187
00:13:04,280 --> 00:13:09,640
or some other kind of issue, we can do a rescue or some other kind of handler.
188
00:13:09,640 --> 00:13:14,040
Because remember, when we are calling this open AI commoner from our controller,
189
00:13:14,040 --> 00:13:17,240
it is expecting some kind of text response.
190
00:13:17,240 --> 00:13:20,439
So this is going to be visible to the end user.
191
00:13:20,439 --> 00:13:23,560
So we can capture whatever kind of errors we want here.
192
00:13:23,560 --> 00:13:27,240
And the nice part about this kind of thing, we can just respond with,
193
00:13:27,240 --> 00:13:31,079
I'm sorry, but I cannot give a response based on the information provided,
194
00:13:31,079 --> 00:13:33,480
or some other kind of meaningful error.
195
00:13:33,480 --> 00:13:37,640
But if we go back and look at the comments controller for the open AI,
196
00:13:37,640 --> 00:13:42,360
remember, we are creating this comment as that particular user.
197
00:13:42,360 --> 00:13:46,760
So if the user has the ability to delete their own comments,
198
00:13:46,760 --> 00:13:51,880
then they're going to be able to also delete the comment that was created by the AI.
199
00:13:51,880 --> 00:13:56,680
So for that reason, I would actually be okay with having some kind of text like this,
200
00:13:56,680 --> 00:14:02,360
because if the user who created or asked the bot to make a response,
201
00:14:02,360 --> 00:14:06,440
doesn't like the response or it wasn't what they were looking for,
202
00:14:06,440 --> 00:14:09,000
then they could delete it and move on.
203
00:14:09,000 --> 00:14:13,640
But before we create this API client, we do need to go ahead and create
204
00:14:13,640 --> 00:14:18,600
this headers and the body, because these both are going to be private methods
205
00:14:18,600 --> 00:14:20,040
within this model.
206
00:14:20,040 --> 00:14:22,440
So we have our headers and the body method.
207
00:14:22,440 --> 00:14:25,400
And for the headers, we do need to set the content,
208
00:14:25,400 --> 00:14:29,000
dash type, and this is going to be the application,
209
00:14:29,000 --> 00:14:30,600
or slash JSON.
210
00:14:30,600 --> 00:14:33,800
And then we also need to pass in our authorization.
211
00:14:33,800 --> 00:14:38,680
The authorization is going to be bear with the space,
212
00:14:38,680 --> 00:14:41,560
and then we need to interplay it in our API token
213
00:14:41,560 --> 00:14:43,800
that we are getting from OpenAI.
214
00:14:43,800 --> 00:14:47,560
And we can just make this an environment variable or something like that.
215
00:14:47,560 --> 00:14:49,959
However, this is just a semantics thing.
216
00:14:49,959 --> 00:14:53,079
I really don't like interplating in text like this.
217
00:14:53,079 --> 00:14:56,920
I would rather do it as an array, or we have our bearer,
218
00:14:56,920 --> 00:14:58,920
and then we also have our token,
219
00:14:58,920 --> 00:15:02,199
and then I'm just going to join it with a space.
220
00:15:02,199 --> 00:15:04,599
And so for me, I think that reads better,
221
00:15:04,599 --> 00:15:07,240
and it's easier to just work around in my opinion,
222
00:15:07,240 --> 00:15:10,200
but you can interplay it in if you want to.
223
00:15:10,200 --> 00:15:13,240
And for the body, we're also just going to make a JSON.
224
00:15:13,240 --> 00:15:15,640
We need to pass in some kind of model,
225
00:15:15,640 --> 00:15:18,280
and their documentation is actually very good.
226
00:15:18,280 --> 00:15:18,840
I like it.
227
00:15:18,840 --> 00:15:21,720
It explains all the different models that you can use.
228
00:15:21,720 --> 00:15:24,600
And in our case, I want to use the DaVinci model,
229
00:15:24,600 --> 00:15:28,840
and its code is text-divinci-003.
230
00:15:28,840 --> 00:15:32,440
We take in our prompt, and that prompt is going to be our instance
231
00:15:32,440 --> 00:15:33,640
variable prompt.
232
00:15:33,640 --> 00:15:35,480
And then the max tokens,
233
00:15:35,480 --> 00:15:38,600
this is basically going to be an integer value,
234
00:15:38,600 --> 00:15:42,120
and that's going to be the number of words in our response
235
00:15:42,120 --> 00:15:44,200
that we're getting from OpenAI.
236
00:15:44,200 --> 00:15:46,280
So if you set this to be too large,
237
00:15:46,280 --> 00:15:49,000
it's going to get expensive very fast.
238
00:15:49,000 --> 00:15:52,120
So I would set it to some kind of reasonable amount.
239
00:15:52,120 --> 00:15:54,920
100 is probably going to be a bit too low,
240
00:15:54,920 --> 00:15:59,000
but you can experiment with what's going to be the expected response.
241
00:15:59,000 --> 00:16:01,880
And then we also are going to pass in a temperature.
242
00:16:01,880 --> 00:16:05,480
And I think it's worth going into the docs for OpenAI,
243
00:16:05,480 --> 00:16:09,240
just so we can see what the completions API is expecting,
244
00:16:09,240 --> 00:16:11,560
because there's a lot more options here.
245
00:16:11,560 --> 00:16:13,320
There's our max tokens,
246
00:16:13,320 --> 00:16:17,000
which you can see it defaults to 16 if we did not include it,
247
00:16:17,000 --> 00:16:18,439
but then the temperature.
248
00:16:18,439 --> 00:16:21,400
It's going to take a number between 0 and 2.
249
00:16:21,400 --> 00:16:25,080
A higher value will make the output more random,
250
00:16:25,080 --> 00:16:29,240
while a lower value will make it more focus and deterministic.
251
00:16:29,240 --> 00:16:31,880
And there's other ones that you can use as well to tweak,
252
00:16:31,880 --> 00:16:33,880
basically how it's going to work.
253
00:16:33,880 --> 00:16:37,640
One that you may want to look at or use is the presence penalty,
254
00:16:37,640 --> 00:16:39,560
and the frequency penalty,
255
00:16:39,560 --> 00:16:43,800
but ultimately it'll be up to you on how you wanted to tweak the settings
256
00:16:43,800 --> 00:16:46,600
to get the desired responses.
257
00:16:46,600 --> 00:16:49,240
And so that's basically all we have to do
258
00:16:49,240 --> 00:16:52,040
to get the AI to generate the response.
259
00:16:52,040 --> 00:16:54,920
Next we have to create that client API,
260
00:16:54,920 --> 00:16:57,800
but that simply just making a post request
261
00:16:57,800 --> 00:17:03,079
to open AI's endpoint with the headers and the body.
262
00:17:03,079 --> 00:17:04,760
So I'm going to create another file
263
00:17:04,760 --> 00:17:08,680
on our models called the API client.rb.
264
00:17:08,680 --> 00:17:10,919
It'll be a class API client,
265
00:17:10,919 --> 00:17:13,960
and this is actually going to be more code than the open AI
266
00:17:13,960 --> 00:17:16,839
cometer, because we are going to take in
267
00:17:16,839 --> 00:17:18,839
a few different things into account.
268
00:17:18,839 --> 00:17:20,839
Well, first initializes,
269
00:17:20,839 --> 00:17:24,040
remember we had our URL, the headers,
270
00:17:24,040 --> 00:17:26,520
and the headers if we don't pass in anything,
271
00:17:26,520 --> 00:17:29,320
just so we don't get any kind of no class errors.
272
00:17:29,320 --> 00:17:31,480
I'm going to set it to an empty hash,
273
00:17:31,480 --> 00:17:34,200
and we're also going to do the same thing for the body.
274
00:17:34,200 --> 00:17:36,360
We'll set our instance variable URL,
275
00:17:36,360 --> 00:17:39,560
is equal to the URL for our URL.
276
00:17:39,560 --> 00:17:42,360
We have our headers is equal to the headers,
277
00:17:42,360 --> 00:17:45,320
and we'll have our body is equal to the body,
278
00:17:45,320 --> 00:17:48,200
but I'm also going to pass to JSON on here,
279
00:17:48,200 --> 00:17:50,040
just so it's properly formatted.
280
00:17:50,040 --> 00:17:53,080
And so we're going to handle a few different types of requests.
281
00:17:53,080 --> 00:17:55,000
We're going to have a get request,
282
00:17:55,000 --> 00:17:57,480
we're going to have a post, a put,
283
00:17:57,480 --> 00:17:59,480
and let's also handle the delete.
284
00:17:59,480 --> 00:18:01,000
So in each one of these cases,
285
00:18:01,000 --> 00:18:03,320
they're going to be instance methods,
286
00:18:03,320 --> 00:18:06,760
and whenever you call the get post, putter delete,
287
00:18:06,760 --> 00:18:09,880
we're just going to make a request passing in,
288
00:18:09,880 --> 00:18:12,440
the appropriate get post, putter delete.
289
00:18:12,440 --> 00:18:15,000
And when I have a single line method like this,
290
00:18:15,000 --> 00:18:17,320
we're as a very simple definition,
291
00:18:17,320 --> 00:18:20,760
I like using the semicolon to just make a one line,
292
00:18:20,760 --> 00:18:23,960
instead of having it multiple lines like this,
293
00:18:23,960 --> 00:18:26,920
because I think that's just going to make it vertically longer,
294
00:18:26,920 --> 00:18:28,120
and it's not necessary.
295
00:18:28,120 --> 00:18:31,400
You can see just kind of add a glance what this is doing,
296
00:18:31,400 --> 00:18:32,760
and what to expect.
297
00:18:32,760 --> 00:18:34,680
We then have our private method,
298
00:18:34,680 --> 00:18:36,920
and we'll first have the request.
299
00:18:36,920 --> 00:18:40,280
We're going to take in our method for that request,
300
00:18:40,280 --> 00:18:42,520
and then we need to build that request.
301
00:18:42,520 --> 00:18:45,000
So I'm going to make a build request,
302
00:18:45,000 --> 00:18:46,840
taking in the method again,
303
00:18:46,840 --> 00:18:50,520
and that's just where we're going to do a case statement on the method.
304
00:18:50,520 --> 00:18:53,320
Because depending on what we're going to use,
305
00:18:53,320 --> 00:18:56,120
it is going to vary on how this is going to look.
306
00:18:56,120 --> 00:18:59,000
So we can have when it's a get request,
307
00:18:59,000 --> 00:19:00,919
when it is a post,
308
00:19:00,919 --> 00:19:02,280
when it's a putt,
309
00:19:02,280 --> 00:19:04,760
and also when it is a delete.
310
00:19:04,760 --> 00:19:06,600
If it is a get request,
311
00:19:06,600 --> 00:19:11,320
then we can respond with the net HTTP get,
312
00:19:11,320 --> 00:19:14,200
and then we can create a new instance of that,
313
00:19:14,200 --> 00:19:17,639
passing in our URL and the headers.
314
00:19:17,639 --> 00:19:19,480
If it's a post request,
315
00:19:19,480 --> 00:19:21,720
we actually want to make this a post.
316
00:19:21,720 --> 00:19:24,280
It's still going to take in the URL and headers,
317
00:19:24,280 --> 00:19:27,320
but we also need to set the body for this,
318
00:19:27,320 --> 00:19:30,600
and so we can call the request.body,
319
00:19:30,600 --> 00:19:32,520
and set that equal to our body.
320
00:19:32,520 --> 00:19:33,800
If it's a putt,
321
00:19:33,800 --> 00:19:36,840
then we need to do the same thing as the post,
322
00:19:36,840 --> 00:19:40,440
except we'll just change this class to a putt,
323
00:19:40,440 --> 00:19:41,880
and if it's a delete,
324
00:19:41,880 --> 00:19:44,600
that's going to be very similar to where it get requests,
325
00:19:44,600 --> 00:19:46,680
but we'll call the delete instead.
326
00:19:46,680 --> 00:19:50,520
And so we could just return the request here,
327
00:19:50,520 --> 00:19:52,360
and that's all we should have to do,
328
00:19:52,360 --> 00:19:54,520
but I think this looks a little bit funny
329
00:19:54,520 --> 00:19:57,800
having the request down here like this multiple times.
330
00:19:57,800 --> 00:20:00,520
So instead, it's a little bit strange,
331
00:20:00,520 --> 00:20:03,639
but I think I would just rather set the request
332
00:20:03,639 --> 00:20:05,240
on each one of these,
333
00:20:05,240 --> 00:20:06,600
and that should work,
334
00:20:06,600 --> 00:20:07,960
or if we wanted to,
335
00:20:07,960 --> 00:20:10,920
we could just set the request up here like this,
336
00:20:10,920 --> 00:20:12,200
on the case,
337
00:20:12,200 --> 00:20:14,840
but then we're going to have to remove this body
338
00:20:14,840 --> 00:20:17,320
on both the putt and the post,
339
00:20:17,320 --> 00:20:20,440
and then we can do a check if we can then check if it's
340
00:20:20,440 --> 00:20:22,440
a putt, or a post,
341
00:20:22,440 --> 00:20:26,440
we'll make it array and just check to see if it includes that method.
342
00:20:26,440 --> 00:20:29,000
Then we return the request.
343
00:20:29,000 --> 00:20:31,320
And so you can do this however you want,
344
00:20:31,320 --> 00:20:33,240
it's really just a matter of styling,
345
00:20:33,240 --> 00:20:36,120
as long as the application is consistent,
346
00:20:36,120 --> 00:20:38,760
with whatever kind of styling you're using here,
347
00:20:38,760 --> 00:20:42,440
then it would be fine by me with whatever direction you went.
348
00:20:42,440 --> 00:20:44,920
So we're going to run the build request,
349
00:20:44,920 --> 00:20:49,160
and we'll set our request as equal to that build request passing in the method.
350
00:20:49,160 --> 00:20:53,320
We can then take our HTTP is equal to the net,
351
00:20:53,320 --> 00:20:59,400
and then HTTP will create a new instance passing in our URL.host,
352
00:20:59,400 --> 00:21:02,200
and also passing in the URL.port.
353
00:21:02,200 --> 00:21:04,040
We can then set the use,
354
00:21:04,040 --> 00:21:08,760
underscore SSL is equal to the URL.scheme,
355
00:21:08,760 --> 00:21:13,800
and we just want to do a check if that is equal to HTTPS.
356
00:21:13,800 --> 00:21:17,320
We can then set some kind of response is equal to
357
00:21:17,320 --> 00:21:22,200
the HTTPS.request passing in our request.
358
00:21:22,200 --> 00:21:24,679
And then we need to handle that response.
359
00:21:24,679 --> 00:21:27,960
So this is going to be another private method that we create,
360
00:21:27,960 --> 00:21:29,960
passing in the response.
361
00:21:29,960 --> 00:21:34,280
And there is a possibility that the request that we make
362
00:21:34,280 --> 00:21:40,040
to the open AI or some other API endpoint is going to timeout.
363
00:21:40,040 --> 00:21:43,480
So what I like to do is to have some kind of rescue
364
00:21:43,480 --> 00:21:46,760
and in our case with net HTTP,
365
00:21:46,760 --> 00:21:48,760
we can rescue from the timeout.
366
00:21:48,760 --> 00:21:50,520
We can then raise the error.
367
00:21:50,520 --> 00:21:54,360
I'm going to create a standard error called API error,
368
00:21:54,360 --> 00:21:58,040
and then we'll just call this the operation time down.
369
00:21:58,040 --> 00:22:00,440
So we do need to create that API error,
370
00:22:00,440 --> 00:22:03,160
and that's just going to be a class API error
371
00:22:03,160 --> 00:22:05,320
inheriting from the standard error,
372
00:22:05,320 --> 00:22:07,879
and we don't need to have anything in there.
373
00:22:07,879 --> 00:22:10,200
So then we need to handle our response.
374
00:22:10,200 --> 00:22:12,600
Again, we'll make that a private method,
375
00:22:12,600 --> 00:22:16,360
and let's just return the JSON dot parse
376
00:22:16,360 --> 00:22:22,040
response.body if it was a net HTTP success.
377
00:22:22,040 --> 00:22:25,560
If it wasn't a success, then we're not going to return,
378
00:22:25,560 --> 00:22:27,399
and then we could do something else here.
379
00:22:27,399 --> 00:22:29,639
We could say some kind of error message,
380
00:22:29,639 --> 00:22:34,439
and we can set this to go to the API request failed with code,
381
00:22:34,439 --> 00:22:37,959
and then we can't interplay it in response.code,
382
00:22:37,959 --> 00:22:42,280
and then we could also interplay it in the response.message.
383
00:22:42,280 --> 00:22:46,760
We can raise the API error with that error message,
384
00:22:46,760 --> 00:22:49,320
but then we also want to handle in case
385
00:22:49,320 --> 00:22:52,360
if we are not getting the proper response.
386
00:22:52,360 --> 00:22:54,840
So for not getting a good response,
387
00:22:54,840 --> 00:22:57,080
meaning that the JSON parsing will fail,
388
00:22:57,080 --> 00:23:00,520
we could also rescue from the JSON parser,
389
00:23:00,520 --> 00:23:03,720
and then we can raise to the API error
390
00:23:03,720 --> 00:23:07,080
that we were unable to parse the JSON response.
391
00:23:07,080 --> 00:23:09,480
And so while this was a bit more,
392
00:23:09,480 --> 00:23:11,879
then the actual API commenter,
393
00:23:11,880 --> 00:23:14,440
I think we are doing two very different things,
394
00:23:14,440 --> 00:23:17,560
and the reason why I like breaking it up like this
395
00:23:17,560 --> 00:23:22,760
is because we have so much going on around that HTTP within here.
396
00:23:22,760 --> 00:23:27,240
Sure, we could use something like HTTP party or another gym.
397
00:23:27,240 --> 00:23:29,960
However, I really don't think that's necessary,
398
00:23:29,960 --> 00:23:34,280
especially once we create some kind of API client like this.
399
00:23:34,280 --> 00:23:38,920
I am going to require the net HTTP at the top here.
400
00:23:38,920 --> 00:23:42,360
Just in case if we're not requiring that else we're in our application,
401
00:23:42,360 --> 00:23:47,480
we don't want to get an error raised that the net HTTP doesn't exist.
402
00:23:47,480 --> 00:23:49,800
It is part of the standard Ruby library,
403
00:23:49,800 --> 00:23:53,160
so there is no external gym that we have to require.
404
00:23:53,160 --> 00:23:56,360
And so once you say you're open a i access token,
405
00:23:56,360 --> 00:23:57,560
don't forget to do that.
406
00:23:57,560 --> 00:23:59,320
We can come in and test this out.
407
00:23:59,320 --> 00:24:01,640
We do what is drifting Ruby.
408
00:24:01,640 --> 00:24:03,160
We'll create our comment,
409
00:24:03,160 --> 00:24:05,720
and then we can ask a i to respond.
410
00:24:05,720 --> 00:24:09,320
It'll take a moment because it is making a API request
411
00:24:09,320 --> 00:24:11,160
and doing some AI generation,
412
00:24:11,160 --> 00:24:13,000
but then we get our response.
413
00:24:13,000 --> 00:24:16,680
And then we could also just make some other kind of comment,
414
00:24:16,680 --> 00:24:18,280
S the AI to respond,
415
00:24:18,280 --> 00:24:20,680
it will respond to that particular comment,
416
00:24:20,680 --> 00:24:23,080
and then we got the response.
417
00:24:23,080 --> 00:24:24,600
And if we look at our logs,
418
00:24:24,600 --> 00:24:28,520
we can see the response that we got back from open a i.
419
00:24:28,520 --> 00:24:31,880
And just so you know how the open a i does handle its billing,
420
00:24:31,880 --> 00:24:33,480
with the prompt tokens,
421
00:24:33,480 --> 00:24:35,880
that was eight, and the completion token,
422
00:24:35,880 --> 00:24:38,680
so the number of words it said in response,
423
00:24:38,680 --> 00:24:41,720
was 20 for a total tokens of 28,
424
00:24:41,720 --> 00:24:46,280
so you would actually be metered build on that 28 tokens.
425
00:24:46,280 --> 00:24:49,000
And so if you're going to implement something like this,
426
00:24:49,000 --> 00:24:51,720
if that is something that you would want to pass a charge
427
00:24:51,720 --> 00:24:53,480
onto the end user,
428
00:24:53,480 --> 00:24:57,560
whether just for the completion tokens or whatever the situation,
429
00:24:57,560 --> 00:25:02,600
you do have access to the usage from that particular user,
430
00:25:02,600 --> 00:25:05,639
but you do want to be careful because it is a metered billing,
431
00:25:05,639 --> 00:25:08,120
and it could get out of hand pretty quick.
432
00:25:08,120 --> 00:25:10,360
And if I were to refactor this,
433
00:25:10,360 --> 00:25:12,199
if we had a particular need,
434
00:25:12,199 --> 00:25:14,760
let's say for the API client,
435
00:25:14,760 --> 00:25:18,840
we also had a situation where you wanted to make a get request,
436
00:25:18,840 --> 00:25:23,159
you then wanted to make a post request or a put or a delete,
437
00:25:23,159 --> 00:25:27,240
multiple times within one single request from the end user,
438
00:25:27,240 --> 00:25:31,879
we are rebuilding a lot of the net HTTP internals here,
439
00:25:31,880 --> 00:25:34,200
for each one of the things that we're doing.
440
00:25:34,200 --> 00:25:39,000
So one option that you could do is instead of passing in that full URL,
441
00:25:39,000 --> 00:25:41,560
we could just pass in the base URL,
442
00:25:41,560 --> 00:25:45,320
and then on the get request, the post put in delete,
443
00:25:45,320 --> 00:25:47,880
we could just take in the URL I.
444
00:25:47,880 --> 00:25:50,920
We would need to update our request to handle that a bit,
445
00:25:50,920 --> 00:25:55,720
but once we initialize the API client and the class that's consuming it,
446
00:25:55,720 --> 00:25:59,960
we can then basically just have one instance of the net HTTP,
447
00:25:59,960 --> 00:26:02,600
and then make the appropriate requests.
448
00:26:02,600 --> 00:26:05,720
But in my case, that really hasn't come up very often,
449
00:26:05,720 --> 00:26:09,480
because the APIs are often interface with our fairly simple,
450
00:26:09,480 --> 00:26:14,360
you just make a request already providing in some kind of credentials.
451
00:26:14,360 --> 00:26:18,520
If you were doing something where you first had to make an authentication request,
452
00:26:18,520 --> 00:26:20,280
and some other kind of handshake,
453
00:26:20,280 --> 00:26:23,960
then I could see that kind of refactor may make sense,
454
00:26:23,960 --> 00:26:27,800
just so we're not having to do an SSL handshake multiple times,
455
00:26:27,800 --> 00:26:31,159
but that net HTTP instance could be reused.
456
00:26:31,160 --> 00:27:00,840
Well, that's all for this episode. Thanks for watching.
41107
Can't find what you're looking for?
Get subtitles in any language from opensubtitles.com, and translate them here.