Skip to content

Commit f52f1af

Browse files
committed
Merge branch 'llms-v1' into 'main'
adding first version. See merge request dferreir/llms-with-matlab!1
2 parents 362628b + 393ceb1 commit f52f1af

20 files changed

+1798
-0
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
function [text, message, response] = callOpenAIChatAPI(messages, functions, nvp)
2+
% This function is undocumented and will change in a future release
3+
4+
%callOpenAIChatAPI Calls the openAI chat completions API.
5+
%
6+
% MESSAGES and FUNCTIONS should be structs matching the json format
7+
% required by the OpenAI Chat Completions API.
8+
% Ref: https://platform.openai.com/docs/guides/gpt/chat-completions-api
9+
%
10+
% Currently, the supported NVP are, including the equivalent name in the API:
11+
% - FunctionCall (function_call)
12+
% - ModelName (model)
13+
% - Temperature (temperature)
14+
% - TopProbabilityMass (top_p)
15+
% - NumCompletions (n)
16+
% - StopSequences (stop)
17+
% - MaxNumTokens (max_tokens)
18+
% - PresencePenalty (presence_penalty)
19+
% - FrequencyPenalty (frequence_penalty)
20+
% - ApiKey
21+
% More details on the parameters: https://platform.openai.com/docs/api-reference/chat/create
22+
%
23+
% Example
24+
%
25+
% % Create messages struct
26+
% messages = {struct("role", "system",...
27+
% "content", "You are a helpful assistant");
28+
% struct("role", "user", ...
29+
% "content", "What is the edit distance between hi and hello?")};
30+
%
31+
% % Create functions struct
32+
% functions = {struct("name", "editDistance", ...
33+
% "description", "Find edit distance between two strings or documents.", ...
34+
% "parameters", struct( ...
35+
% "type", "object", ...
36+
% "properties", struct(...
37+
% "str1", struct(...
38+
% "description", "Source string.", ...
39+
% "type", "string"),...
40+
% "str2", struct(...
41+
% "description", "Target string.", ...
42+
% "type", "string")),...
43+
% "required", ["str1", "str2"]))};
44+
%
45+
% % Define your API key
46+
% apiKey = "your-api-key-here"
47+
%
48+
% % Send a request
49+
% [text, message] = llms.internal.callOpenAIChatAPI(messages, functions, ApiKey=apiKey)
50+
51+
% Copyright 2023 The MathWorks, Inc.
52+
53+
arguments
54+
messages
55+
functions
56+
nvp.FunctionCall = []
57+
nvp.ModelName = "gpt-3.5-turbo"
58+
nvp.Temperature = 1
59+
nvp.TopProbabilityMass = 1
60+
nvp.NumCompletions = 1
61+
nvp.StopSequences = []
62+
nvp.MaxNumTokens = inf
63+
nvp.PresencePenalty = 0
64+
nvp.FrequencyPenalty = 0
65+
nvp.ApiKey = ""
66+
end
67+
68+
END_POINT = "https://api.openai.com/v1/chat/completions";
69+
70+
parameters = buildParametersCall(messages, functions, nvp);
71+
72+
response = llms.internal.sendRequest(parameters,nvp.ApiKey, END_POINT);
73+
74+
% If call errors, "choices" will not be part of response.Body.Data, instead
75+
% we get response.Body.Data.error
76+
if response.StatusCode=="OK"
77+
% Outputs the first generation
78+
message = response.Body.Data.choices(1).message;
79+
if isfield(message, "function_call")
80+
text = "";
81+
else
82+
text = string(message.content);
83+
end
84+
else
85+
text = "";
86+
message = struct();
87+
end
88+
end
89+
90+
function parameters = buildParametersCall(messages, functions, nvp)
91+
% Builds a struct in the format that is expected by the API, combining
92+
% MESSAGES, FUNCTIONS and parameters in NVP.
93+
94+
parameters = struct();
95+
parameters.messages = messages;
96+
if ~isempty(functions)
97+
parameters.functions = functions;
98+
end
99+
100+
if ~isempty(nvp.FunctionCall)
101+
parameters.function_call = nvp.FunctionCall;
102+
end
103+
104+
parameters.model = nvp.ModelName;
105+
106+
dict = mapNVPToParameters;
107+
108+
nvpOptions = keys(dict);
109+
for i=1:length(nvpOptions)
110+
if isfield(nvp, nvpOptions(i))
111+
parameters.(dict(nvpOptions(i))) = nvp.(nvpOptions(i));
112+
end
113+
end
114+
end
115+
116+
function dict = mapNVPToParameters()
117+
dict = dictionary();
118+
dict("Temperature") = "temperature";
119+
dict("TopProbabilityMass") = "top_p";
120+
dict("NumCompletions") = "n";
121+
dict("StopSequences") = "stop";
122+
dict("MaxNumTokens") = "max_tokens";
123+
dict("PresencePenalty") = "presence_penalty";
124+
dict("FrequencyPenalty ") = "frequency_penalty";
125+
end
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
function key = getApiKeyFromNvpOrEnv(nvp)
2+
% This function is undocumented and will change in a future release
3+
4+
%getApiKeyFromNvpOrEnv Retrieves an API key from a Name-Value Pair struct or environment variable.
5+
%
6+
% This function takes a struct nvp containing name-value pairs and checks
7+
% if it contains a field called "ApiKey". If the field is not found,
8+
% the function attempts to retrieve the API key from an environment
9+
% variable called "OPENAI_API_KEY". If both methods fail, the function
10+
% throws an error.
11+
12+
% Copyright 2023 The MathWorks, Inc.
13+
14+
if isfield(nvp, "ApiKey")
15+
key = nvp.ApiKey;
16+
else
17+
if isenv("OPENAI_API_KEY")
18+
key = getenv("OPENAI_API_KEY");
19+
else
20+
error("llms:keyMustBeSpecified", llms.utils.errorMessageCatalog.getMessage("llms:keyMustBeSpecified"));
21+
end
22+
end
23+
end

+llms/+internal/sendRequest.m

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
function response = sendRequest(parameters, token, endpoint)
2+
% This function is undocumented and will change in a future release
3+
4+
%sendRequest Sends a request to an ENDPOINT using PARAMETERS and
5+
% api key TOKEN.
6+
7+
% Copyright 2023 The MathWorks, Inc.
8+
9+
% Define the headers for the API request
10+
11+
headers = [matlab.net.http.HeaderField('Content-Type', 'application/json')...
12+
matlab.net.http.HeaderField('Authorization', "Bearer " + token)];
13+
% Define the request message
14+
request = matlab.net.http.RequestMessage('post',headers,parameters);
15+
% Send the request and store the response
16+
response = send(request, matlab.net.URI(endpoint));
17+
end
18+

+llms/+utils/errorMessageCatalog.m

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
classdef errorMessageCatalog
2+
% This class is undocumented and will change in a future release
3+
4+
%errorMessageCatalog Stores the error messages from this repository
5+
6+
% Copyright 2023 The MathWorks, Inc.
7+
properties(Constant)
8+
%CATALOG dictionary mapping error ids to error msgs
9+
Catalog = buildErrorMessageCatalog;
10+
end
11+
12+
methods(Static)
13+
function msg = getMessage(messageId, slot)
14+
% This function is undocumented and will change in a future release
15+
16+
%getMessage returns error message given a messageID and a SLOT.
17+
% The value in SLOT should be ordered, where the n-th element
18+
% will replace the value "{n}".
19+
20+
arguments
21+
messageId {mustBeNonzeroLengthText}
22+
end
23+
arguments(Repeating)
24+
slot {mustBeNonzeroLengthText}
25+
end
26+
27+
msg = llms.utils.errorMessageCatalog.Catalog(messageId);
28+
if ~isempty(slot)
29+
for i=1:numel(slot)
30+
msg = replace(msg,"{"+i+"}", slot{i});
31+
end
32+
end
33+
end
34+
end
35+
end
36+
37+
function catalog = buildErrorMessageCatalog
38+
catalog = dictionary("string", "string");
39+
catalog("llms:mustBeUnique") = "Values must be unique.";
40+
catalog("llms:mustBeVarName") = "Parameter name must begin with a letter and contain not more than 'namelengthmax' characters.";
41+
catalog("llms:parameterMustBeUnique") = "A parameter name equivalent to '{1}' already exists in Parameters. Redefining a parameter is not allowed.";
42+
catalog("llms:mustBeAssistantCall") = "Input struct must contain field 'role' with value 'assistant', and field 'content'.";
43+
catalog("llms:mustBeAssistantWithContent") = "Input struct must contain field 'content' containing text with one or more characters.";
44+
catalog("llms:mustBeAssistantWithNameAndArguments") = "Field 'function_call' must be a struct with fields 'name' and 'arguments'.";
45+
catalog("llms:assistantMustHaveTextNameAndArguments") = "Fields 'name' and 'arguments' must be text with one or more characters.";
46+
catalog("llms:mustBeValidIndex") = "Value is larger than the number of elements in Messages ({1}).";
47+
catalog("llms:stopSequencesMustHaveMax4Elements") = "Number of elements must not be larger than 4.";
48+
catalog("llms:keyMustBeSpecified") = "API key not found as environment variable OPEN_API_KEY and not specified via ApiKey parameter.";
49+
catalog("llms:mustHaveMessages") = "Value must contain at least one message in Messages.";
50+
catalog("llms:mustSetFunctionsForCall") = "When no functions are defined, FunctionCall must not be specified.";
51+
catalog("llms:mustBeMessagesOrTxt") = "Messages must be text with one or more characters or an openAIMessages objects.";
52+
end
53+

+llms/+utils/isUnique.m

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
function tf = isUnique(values)
2+
% This function is undocumented and will change in a future release
3+
4+
% Simple function to check if value is unique
5+
6+
% Copyright 2023 The MathWorks, Inc.
7+
tf = numel(values)==numel(unique(values));
8+
end
9+

+llms/+utils/mustBeTextOrEmpty.m

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
function mustBeTextOrEmpty(value)
2+
% This function is undocumented and will change in a future release
3+
4+
% Simple function to check if value is empty or text scalar
5+
6+
% Copyright 2023 The MathWorks, Inc.
7+
if ~isempty(value)
8+
mustBeTextScalar(value)
9+
end
10+
end

SECURITY.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Reporting Security Vulnerabilities
2+
3+
If you believe you have discovered a security vulnerability, please report it to
4+
[security@mathworks.com](mailto:security@mathworks.com). Please see
5+
[MathWorks Vulnerability Disclosure Policy for Security Researchers](https://www.mathworks.com/company/aboutus/policies_statements/vulnerability-disclosure-policy.html)
6+
for additional information.

examples/ExampleAgentCreation.mlx

2.75 KB
Binary file not shown.

examples/ExampleBasicUsage.mlx

2.75 KB
Binary file not shown.

examples/ExampleChatBot.mlx

2.75 KB
Binary file not shown.

0 commit comments

Comments
 (0)