-
Notifications
You must be signed in to change notification settings - Fork 25
/
openAIFunction.m
184 lines (154 loc) · 6.66 KB
/
openAIFunction.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
classdef (Sealed) openAIFunction
%openAIFunction Define a function
%
% FUNC = openAIFunction(NAME, DESCRIPTION) creates an open AI function
% object with the specified name and description.
%
% openAIFunction Functions:
% openAIFunction - Define a function.
% addParameter - Add parameter to the function.
%
% openAIFunction Properties:
% FunctionName - Name of the function.
% Description - Description of the function.
% Parameters - Parameters of function.
%
% Example:
% % Create an OpenAI function object
% func = openAIFunction("editDistance", "Find edit distance between two strings or documents");
%
% % Add two parameters with type and description
% func = addParameter(func, "str1", type="string", description="Source string.");
% func = addParameter(func, "str2", type="string", description="Target string.");
% Copyright 2023 The MathWorks, Inc.
properties(SetAccess=private)
%FUNCTIONNAME Name of the function.
FunctionName
%DESCRIPTION Description of the function.
Description
%PARAMETERS Parameters of function.
Parameters = struct()
end
methods
function this = openAIFunction(name, description)
arguments
name (1,1) {mustBeNonzeroLengthText}
description {llms.utils.mustBeTextOrEmpty} = []
end
this.FunctionName = name;
this.Description = description;
end
function this = addParameter(this, parameterName, propertyName, propertyValue, nvp)
%addParameter Add parameter to the function
%
% FCN = addParameter(FCN,paramName,propName1,value1, ..., propNameN,valueN)
% adds the specified parameter and corresponding property names and values to the
% function signature. The property names must be
% "type", "description", or "enum".
% The values depend on the property name:
% - Values corresponding to "type" must be any combination of "string",
% "number", "integer", "object", "boolean", "null".
% - Values corresponding to "description" must be a string scalar.
% - Value corresponging to "enum" must be string vectors.
%
% FCN = addParameter(__,RequiredParameter=TF), specifies
% if the parameter is a required parameter.
%
% Example:
% % Create an OpenAI function object
% f = openAIFunction("editDistance", "Find edit distance between two strings or documents");
%
% % Add two parameters with type and description
% f = addParameter(f,"str1","type","string","description","Source string.");
% f = addParameter(f,"str2","type","string" ,"description","Target string.");
arguments
this (1,1) openAIFunction
parameterName (1,1) {mustBeNonzeroLengthText, mustBeValidVariableName}
end
arguments(Repeating)
propertyName (1,1) {mustBeNonzeroLengthText, mustBeMember(propertyName, {'type', 'enum', 'description'})}
propertyValue (1,:) {mustBeNonzeroLengthText, validatePropertyValue(propertyValue, propertyName)}
end
arguments
nvp.RequiredParameter (1,1) logical = true
end
if isfield(this.Parameters,parameterName)
error("llms:parameterMustBeUnique", ...
llms.utils.errorMessageCatalog.getMessage("llms:parameterMustBeUnique", parameterName));
end
properties = struct();
% Properties are optional
if ~isempty(propertyName)
for i=1:length(propertyName)
properties.(propertyName{i}) = propertyValue{i};
end
end
this.Parameters.(parameterName) = properties;
this.Parameters.(parameterName).required = nvp.RequiredParameter;
end
end
methods(Hidden)
function funStruct = encodeStruct(this)
%encodeStruct Encode the function object as a struct
funStruct = struct();
funStruct.name = this.FunctionName;
if ~isempty(this.Description)
funStruct.description = this.Description;
end
funStruct.parameters = struct();
% The API requires type="object"
funStruct.parameters.type = "object";
funStruct.parameters.properties = struct();
requiredArguments = [];
parameterNames = string(fieldnames(this.Parameters));
for i=1:length(parameterNames)
parameterStruct = this.Parameters.(parameterNames(i));
if parameterStruct.required
requiredArguments = [requiredArguments,parameterNames(i)]; %#ok
end
% "required" should not be a property when sending to the api
parameterStruct = rmfield(parameterStruct,"required");
% enum needs to be encoded as array
if isfield(parameterStruct, "enum") && numel(parameterStruct.enum)==1
parameterStruct.enum = {parameterStruct.enum};
end
funStruct.parameters.properties.(parameterNames(i)) = parameterStruct;
end
% Only create the "required" field if there are required arguments
if ~isempty(requiredArguments)
funStruct.parameters.required = requiredArguments;
if numel(requiredArguments)==1
% This will force jsonencode to see "required" as an array
funStruct.parameters.required = {funStruct.parameters.required};
end
end
end
end
end
function mustBeValidVariableName(value)
if ~isvarname(value)
error("llms:mustBeVarName", llms.utils.errorMessageCatalog.getMessage("llms:mustBeVarName"));
end
end
function validatePropertyValue(value,name)
switch(name)
case "type"
validatePropertyType(value);
case "description"
validatePropertyDescription(value);
case "enum"
validatePropertyEnum(value);
end
end
function validatePropertyType(value)
validValues = ["string", "number", "integer", "object", "boolean", "null"];
mustBeMember(value, validValues);
end
function validatePropertyDescription(value)
mustBeTextScalar(value)
end
function validatePropertyEnum(value)
if ~llms.utils.isUnique(value)
error("llms:mustBeUnique", llms.utils.errorMessageCatalog.getMessage("llms:mustBeUnique"));
end
end