Skip to content

Commit 1de4200

Browse files
committed
Addressing more comments by ccreutzi
1 parent 3ec2414 commit 1de4200

File tree

4 files changed

+58
-67
lines changed

4 files changed

+58
-67
lines changed

README.md

+12-8
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ history = addUserMessage(history,"Generate MATLAB code that computes that");
121121
122122
### Streaming the response
123123
124-
You can specifying the streaming function when you create the chat assistant. This will print the response to the command window.
124+
Streaming allows you to start receiving the output from the API as it is generated token by token, rather than wait for the entire completion to be generated. You can specifying the streaming function when you create the chat assistant. In this example, the streaming function will print the response to the command window.
125125
```matlab
126126
% streaming function
127127
sf = @(x)fprintf("%s",x);
@@ -188,21 +188,25 @@ ans =
188188
You can then call the function `sind` with the specified argument and return the value to the API add a function message to the history:
189189

190190
```matlab
191-
% Arguments are returned as a json, so you need to decode it first
191+
% Arguments are returned as json, so you need to decode it first
192192
id = string(response.tool_calls.id);
193193
func = string(response.tool_calls.function.name);
194-
args = jsondecode(response.tool_calls.function.arguments);
195-
result = sind(args.x);
196-
messages = addToolMessage(messages,id,func,"x="+result);
197-
[txt, response] = generate(chat, messages);
194+
if func == "sind"
195+
args = jsondecode(response.tool_calls.function.arguments);
196+
result = sind(args.x);
197+
messages = addToolMessage(messages,id,func,"x="+result);
198+
[txt, response] = generate(chat, messages);
199+
else
200+
% handle calls to unknown functions
201+
end
198202
```
199203
200204
The model then will use the function result to generate a more precise response:
201205
202206
```shell
203207
>> txt
204208
205-
text =
209+
txt =
206210
207211
"The sine of 30 degrees is approximately 0.5."
208212
```
@@ -303,7 +307,7 @@ ans =
303307
```
304308
## Getting Started with Images API
305309
306-
To get started, you can either create an `openAIImage` object and use its methods or use it in a more complex setup, as needed.
310+
To get started, you can either create an `openAIImages` object and use its methods or use it in a more complex setup, as needed.
307311
308312
```matlab
309313
mdl = openAIImages(ModelName="dall-e-3");

openAIChat.m

+10-10
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
% StreamFun - Function to callback when streaming the
3535
% result
3636
%
37-
% ResponseFormat - The format of response the model returns.
38-
% Default is text, or json.
37+
% ResponseFormat - The format of response the model returns.
38+
% "text" (default) | "json"
3939
%
4040
% openAIChat Functions:
4141
% openAIChat - Chat completion API from OpenAI.
@@ -98,7 +98,7 @@
9898
%SYSTEMPROMPT System prompt.
9999
SystemPrompt = []
100100

101-
%RESPONSEFORMAT Response format, text or json
101+
%RESPONSEFORMAT Response format, "text" or "json"
102102
ResponseFormat
103103
end
104104

@@ -121,7 +121,7 @@
121121
nvp.Temperature {mustBeValidTemperature} = 1
122122
nvp.TopProbabilityMass {mustBeValidTopP} = 1
123123
nvp.StopSequences {mustBeValidStop} = {}
124-
nvp.ResponseFormat (1,1) {mustBeMember(nvp.ResponseFormat,["text","json"])} = "text"
124+
nvp.ResponseFormat (1,1) string {mustBeMember(nvp.ResponseFormat,["text","json"])} = "text"
125125
nvp.ApiKey {mustBeNonzeroLengthTextScalar}
126126
nvp.PresencePenalty {mustBeValidPenalty} = 0
127127
nvp.FrequencyPenalty {mustBeValidPenalty} = 0
@@ -139,17 +139,17 @@
139139
this.StreamFun = [];
140140
end
141141

142-
if ~isempty(nvp.Tools)
142+
if isempty(nvp.Tools)
143+
this.Tools = [];
144+
this.FunctionsStruct = [];
145+
this.FunctionNames = [];
146+
else
143147
this.Tools = nvp.Tools;
144148
[this.FunctionsStruct, this.FunctionNames] = functionAsStruct(nvp.Tools);
145149
if strcmp(nvp.ModelName,'gpt-4-vision-preview')
146150
error("llms:invalidOptionAndValueForModel", ...
147151
llms.utils.errorMessageCatalog.getMessage("llms:invalidOptionForModel", "Tools", nvp.ModelName));
148152
end
149-
else
150-
this.Tools = [];
151-
this.FunctionsStruct = [];
152-
this.FunctionNames = [];
153153
end
154154

155155
if ~isempty(systemPrompt)
@@ -316,7 +316,7 @@ function mustBeValidFunctionCall(this, functionCall)
316316
if ~isempty(this.Tools)
317317
toolChoice = "auto";
318318
end
319-
else
319+
elseif ToolChoice ~= "auto"
320320
% if toolChoice is not empty, then it must be in the format
321321
% {"type": "function", "function": {"name": "my_function"}}
322322
toolChoice = struct("type","function","function",struct("name",toolChoice));

openAIImages.m

+27-40
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@
3131
% Copyright 2024 The MathWorks, Inc.
3232

3333
properties(SetAccess=private)
34-
%MODELNAME Model name.
34+
%ModelName Model name.
3535
ModelName
3636

37-
%TIMEOUT Connection timeout in seconds (default 10 secs)
37+
%TimeOut Connection timeout in seconds (default 10 secs)
3838
TimeOut
3939
end
4040

@@ -77,7 +77,7 @@
7777
% 1792x1024, or 1024x1792
7878
%
7979
% Quality - Quality of the images to generate.
80-
% "standard" (default) or 'hd'.
80+
% "standard" (default) or "hd".
8181
% Only "dall-e-3" supports this parameter.
8282
%
8383
% Style - The style of the generated images.
@@ -89,11 +89,11 @@
8989
prompt {mustBeTextScalar}
9090
nvp.NumImages (1,1) {mustBePositive, mustBeInteger,...
9191
mustBeLessThanOrEqual(nvp.NumImages,10)} = 1
92-
nvp.Size (1,1) {mustBeMember(nvp.Size, ["256x256", "512x512", ...
92+
nvp.Size (1,1) string {mustBeMember(nvp.Size, ["256x256", "512x512", ...
9393
"1024x1024", "1792x1024", ...
9494
"1024x1792"])} = "1024x1024"
95-
nvp.Quality (1,1) {mustBeMember(nvp.Quality,["standard", "hd"])}
96-
nvp.Style (1,1) {mustBeMember(nvp.Style,["vivid", "natural"])}
95+
nvp.Quality (1,1) string {mustBeMember(nvp.Quality,["standard", "hd"])}
96+
nvp.Style (1,1) string {mustBeMember(nvp.Style,["vivid", "natural"])}
9797
end
9898

9999
endpoint = "https://api.openai.com/v1/images/generations";
@@ -106,7 +106,7 @@
106106
"n",nvp.NumImages,...
107107
"size",nvp.Size);
108108

109-
if this.ModelName~="dall-e-3"
109+
if this.ModelName=="dall-e-2"
110110
% dall-e-3 only params
111111
if isfield(nvp, "Quality")
112112
error("llms:invalidOptionForModel", ...
@@ -180,7 +180,7 @@
180180
nvp.MaskImagePath {mustBeValidFileType(nvp.MaskImagePath)}
181181
nvp.NumImages (1,1) {mustBePositive, mustBeInteger,...
182182
mustBeLessThanOrEqual(nvp.NumImages,10)} = 1
183-
nvp.Size (1,1) {mustBeMember(nvp.Size,["256x256", ...
183+
nvp.Size (1,1) string {mustBeMember(nvp.Size,["256x256", ...
184184
"512x512", ...
185185
"1024x1024"])} = "1024x1024"
186186
end
@@ -197,15 +197,12 @@
197197
endpoint = 'https://api.openai.com/v1/images/edits';
198198

199199
% Required params
200+
numImages = num2str(nvp.NumImages);
200201
body = matlab.net.http.io.MultipartFormProvider( ...
201202
'image',matlab.net.http.io.FileProvider(imagePath), ...
202-
'prompt',matlab.net.http.io.FormProvider(prompt));
203-
204-
body.Names = [body.Names,"n"];
205-
numImages = num2str(nvp.NumImages);
206-
body.Parts = [body.Parts,{matlab.net.http.io.FormProvider(numImages)}];
207-
body.Names = [body.Names,"size"];
208-
body.Parts = [body.Parts,{matlab.net.http.io.FormProvider(nvp.Size)}];
203+
'prompt',matlab.net.http.io.FormProvider(prompt), ...
204+
'n',matlab.net.http.io.FormProvider(numImages),...
205+
'size',matlab.net.http.io.FormProvider(nvp.Size));
209206

210207
% Optional param
211208
if isfield(nvp,"MaskImagePath")
@@ -230,21 +227,21 @@
230227
% and square.
231228
%
232229
% [IMAGES, RESPONSE] = createVariation(__, Name=Value) specifies
233-
% additiona options.
230+
% additional options.
234231
%
235232
% NumImages - Number of images to generate.
236233
% Default value is 1. The max is 10.
237234
%
238235
% Size - Size of the generated images.
239-
% Must be one of 256x256, 512x512, or
240-
% 1024x1024 (default)
236+
% Must be one of "256x256", "512x512", or
237+
% "1024x1024" (default)
241238

242239
arguments
243240
this (1,1) openAIImages
244241
imagePath {mustBeValidFileType(imagePath)}
245242
nvp.NumImages (1,1) {mustBePositive, mustBeInteger,...
246243
mustBeLessThanOrEqual(nvp.NumImages,10)} = 1
247-
nvp.Size (1,1) {mustBeMember(nvp.Size,["256x256", ...
244+
nvp.Size (1,1) string {mustBeMember(nvp.Size,["256x256", ...
248245
"512x512","1024x1024"])} = "1024x1024"
249246
end
250247

@@ -257,15 +254,11 @@
257254

258255
endpoint = 'https://api.openai.com/v1/images/variations';
259256

260-
body = matlab.net.http.io.MultipartFormProvider('image', ...
261-
matlab.net.http.io.FileProvider(imagePath));
262-
263-
body.Names = [body.Names,"n"];
264257
numImages = num2str(nvp.NumImages);
265-
body.Parts = [body.Parts,{matlab.net.http.io.FormProvider(numImages)}];
266-
267-
body.Names = [body.Names,"size"];
268-
body.Parts = [body.Parts,{matlab.net.http.io.FormProvider(nvp.Size)}];
258+
body = matlab.net.http.io.MultipartFormProvider(...
259+
'image',matlab.net.http.io.FileProvider(imagePath),...
260+
'n',matlab.net.http.io.FormProvider(numImages),...
261+
'size',matlab.net.http.io.FormProvider(nvp.Size));
269262

270263
% Send the HTTP Request
271264
response = sendRequest(this, endpoint, body);
@@ -274,16 +267,13 @@
274267
end
275268

276269
function response = sendRequest(this, endpoint, body)
277-
%getResponse get the response from the given endpoint
270+
%sendRequest send request to the given endpoint, return response
271+
headers = matlab.net.http.HeaderField('Authorization', "Bearer " + this.ApiKey);
278272
if isa(body,'struct')
279-
headers = matlab.net.http.HeaderField('Content-Type', 'application/json');
280-
headers(2) = matlab.net.http.HeaderField('Authorization', "Bearer " + this.ApiKey);
281-
else
282-
headers = matlab.net.http.HeaderField('Authorization', "Bearer " + this.ApiKey);
273+
headers(2) = matlab.net.http.HeaderField('Content-Type', 'application/json');
283274
end
284275
request = matlab.net.http.RequestMessage('post', headers, body);
285276

286-
% Create a HTTPOptions object;
287277
httpOpts = matlab.net.http.HTTPOptions;
288278
httpOpts.ConnectTimeout = this.TimeOut;
289279
response = send(request, matlab.net.URI(endpoint), httpOpts);
@@ -304,14 +294,11 @@ function mustBeValidSize(this, imagesize)
304294

305295
function images = extractImages(response)
306296

307-
if response.StatusCode=="OK"
297+
if response.StatusCode=="OK" && isfield(response.Body.Data.data,"url")
308298
% Output the images
309299
if isfield(response.Body.Data.data,"url")
310300
urls = arrayfun(@(x) string(x.url), response.Body.Data.data);
311-
images = cell(1,numel(urls));
312-
for ii = 1:numel(urls)
313-
images{ii} = imread(urls(ii));
314-
end
301+
images = arrayfun(@imread,urls,UniformOutput=false);
315302
else
316303
images = [];
317304
end
@@ -322,13 +309,13 @@ function mustBeValidSize(this, imagesize)
322309
end
323310

324311
function validateSizeNVP(model, size)
325-
if ismember(size,["1792x1024", "1024x1792"]) && model~="dall-e-3"
312+
if ismember(size,["1792x1024", "1024x1792"]) && model=="dall-e-2"
326313
error("llms:invalidOptionAndValueForModel", ...
327314
llms.utils.errorMessageCatalog.getMessage("llms:invalidOptionAndValueForModel", ...
328315
"Size", size, model));
329316
end
330317

331-
if ismember(size,["256x256", "512x512"]) && model~="dall-e-2"
318+
if ismember(size,["256x256", "512x512"]) && model=="dall-e-3"
332319
error("llms:invalidOptionAndValueForModel", ...
333320
llms.utils.errorMessageCatalog.getMessage("llms:invalidOptionAndValueForModel", ...
334321
"Size", size, model));

openAIMessages.m

+9-9
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
% addUserMessage - Add user message.
88
% addUserMessageWithImages - Add user message with images for
99
% GPT-4 Turbo with Vision.
10-
% addFunctionMessage - Add a function message.
10+
% addToolMessage - Add a tool message.
1111
% addResponseMessage - Add a response message.
1212
% removeMessage - Remove message from history.
1313
%
@@ -97,7 +97,7 @@
9797
% messages = openAIMessages;
9898
%
9999
% % Add user message with an image
100-
% content = "What are in this picture?"
100+
% content = "What is in this picture?"
101101
% images = "peppers.png"
102102
% messages = addUserMessageWithImages(messages, content, images);
103103
%
@@ -108,21 +108,21 @@
108108
this (1,1) openAIMessages
109109
content {mustBeNonzeroLengthTextScalar}
110110
images (1,:) {mustBeNonzeroLengthText}
111-
nvp.Detail {mustBeMember(nvp.Detail,["low","high","auto"])} = "auto"
111+
nvp.Detail string {mustBeMember(nvp.Detail,["low","high","auto"])} = "auto"
112112
end
113113

114114
newMessage = struct("role", "user", "content", []);
115115
newMessage.content = {struct("type","text","text",string(content))};
116-
for i = 1:numel(images)
117-
if startsWith(images(i),("https://"|"http://"))
116+
for img = images(:).'
117+
if startsWith(img,("https://"|"http://"))
118118
s = struct( ...
119119
"type","image_url", ...
120-
"image_url",struct("url",images{i}));
120+
"image_url",struct("url",img));
121121
else
122-
[~,~,ext] = fileparts(images(i));
122+
[~,~,ext] = fileparts(img);
123123
MIMEType = "data:image/" + erase(ext,".") + ";base64,";
124124
% Base64 encode the image using the given MIME type
125-
fid = fopen(images(i));
125+
fid = fopen(img);
126126
im = fread(fid,'*uint8');
127127
fclose(fid);
128128
b64 = matlab.net.base64encode(im);
@@ -142,7 +142,7 @@
142142
function this = addToolMessage(this, id, name, content)
143143
%addToolMessage Add Tool message.
144144
%
145-
% MESSAGES = addFunctionMessage(MESSAGES, ID, NAME, CONTENT)
145+
% MESSAGES = addToolMessage(MESSAGES, ID, NAME, CONTENT)
146146
% adds a tool message with the specified id, name and content.
147147
% ID, NAME and CONTENT must be text scalars.
148148
%

0 commit comments

Comments
 (0)