|
1 |
| -classdef (Abstract) hopenAIChat < matlab.unittest.TestCase |
| 1 | +classdef (Abstract) hopenAIChat < hstructuredOutput |
2 | 2 | % Tests for OpenAI-based chats (openAIChat, azureChat)
|
3 | 3 |
|
4 | 4 | % Copyright 2023-2024 The MathWorks, Inc.
|
|
17 | 17 | constructor
|
18 | 18 | defaultModel
|
19 | 19 | visionModel
|
20 |
| - structuredModel |
21 |
| - noStructuredOutputModel |
22 | 20 | end
|
23 | 21 |
|
24 | 22 | methods(Test)
|
@@ -195,66 +193,6 @@ function generateOverridesProperties(testCase)
|
195 | 193 | testCase.verifyThat(text, EndsWithSubstring("3, "));
|
196 | 194 | end
|
197 | 195 |
|
198 |
| - function generateWithStructuredOutput(testCase) |
199 |
| - import matlab.unittest.constraints.IsEqualTo |
200 |
| - import matlab.unittest.constraints.StartsWithSubstring |
201 |
| - res = generate(testCase.structuredModel,"Which animal produces honey?",... |
202 |
| - ResponseFormat = struct(commonName = "dog", scientificName = "Canis familiaris")); |
203 |
| - testCase.assertClass(res,"struct"); |
204 |
| - testCase.verifySize(fieldnames(res),[2,1]); |
205 |
| - testCase.verifyThat(res.commonName, IsEqualTo("Honeybee") | IsEqualTo("Honey bee") | IsEqualTo("Honey Bee")); |
206 |
| - testCase.verifyThat(res.scientificName, StartsWithSubstring("Apis")); |
207 |
| - end |
208 |
| - |
209 |
| - function generateListWithStructuredOutput(testCase) |
210 |
| - prototype = struct("plantName",{"appletree","pear"}, ... |
211 |
| - "fruit",{"apple","pear"}, ... |
212 |
| - "edible",[true,true], ... |
213 |
| - "ignore", missing); |
214 |
| - res = generate(testCase.structuredModel,"What is harvested in August?", ResponseFormat = prototype); |
215 |
| - testCase.verifyCompatibleStructs(res, prototype); |
216 |
| - end |
217 |
| - |
218 |
| - function generateWithNestedStructs(testCase) |
219 |
| - stepsPrototype = struct("explanation",{"a","b"},"assumptions",{"a","b"}); |
220 |
| - prototype = struct("steps",stepsPrototype,"final_answer","a"); |
221 |
| - res = generate(testCase.structuredModel,"What is the positive root of x^2-2*x+1?", ... |
222 |
| - ResponseFormat=prototype); |
223 |
| - testCase.verifyCompatibleStructs(res,prototype); |
224 |
| - end |
225 |
| - |
226 |
| - function incompleteJSONResponse(testCase) |
227 |
| - country = ["USA";"UK"]; |
228 |
| - capital = ["Washington, D.C.";"London"]; |
229 |
| - population = [345716792;69203012]; |
230 |
| - prototype = struct("country",country,"capital",capital,"population",population); |
231 |
| - |
232 |
| - testCase.verifyError(@() generate(testCase.structuredModel, ... |
233 |
| - "What are the five largest countries whose English names" + ... |
234 |
| - " start with the letter A?", ... |
235 |
| - ResponseFormat = prototype, MaxNumTokens=3), "llms:apiReturnedIncompleteJSON"); |
236 |
| - end |
237 |
| - |
238 |
| - function generateWithExplicitSchema(testCase) |
239 |
| - import matlab.unittest.constraints.IsSameSetAs |
240 |
| - schema = iGetSchema(); |
241 |
| - |
242 |
| - genUser = generate(testCase.structuredModel,"Create a sample user",ResponseFormat=schema); |
243 |
| - genAddress = generate(testCase.structuredModel,"Create a sample address",ResponseFormat=schema); |
244 |
| - |
245 |
| - testCase.verifyClass(genUser,"string"); |
246 |
| - genUserDecoded = jsondecode(genUser); |
247 |
| - testCase.verifyClass(genUserDecoded.item,"struct"); |
248 |
| - testCase.verifyThat(fieldnames(genUserDecoded.item),... |
249 |
| - IsSameSetAs({'name','age'})); |
250 |
| - |
251 |
| - testCase.verifyClass(genAddress,"string"); |
252 |
| - genAddressDecoded = jsondecode(genAddress); |
253 |
| - testCase.verifyClass(genAddressDecoded.item,"struct"); |
254 |
| - testCase.verifyThat(fieldnames(genAddressDecoded.item),... |
255 |
| - IsSameSetAs({'number','street','city'})); |
256 |
| - end |
257 |
| - |
258 | 196 | function invalidInputsGenerate(testCase, InvalidGenerateInput)
|
259 | 197 | f = openAIFunction("validfunction");
|
260 | 198 | chat = testCase.constructor(Tools=f, APIKey="this-is-not-a-real-key");
|
@@ -321,89 +259,4 @@ function keyNotFound(testCase)
|
321 | 259 | testCase.verifyError(testCase.constructor, "llms:keyMustBeSpecified");
|
322 | 260 | end
|
323 | 261 | end
|
324 |
| - |
325 |
| - methods |
326 |
| - function verifyCompatibleStructs(testCase,data,prototype) |
327 |
| - import matlab.unittest.constraints.IsSameSetAs |
328 |
| - testCase.assertClass(data,"struct"); |
329 |
| - if ~isscalar(data) |
330 |
| - arrayfun(@(d) testCase.verifyCompatibleStructs(d,prototype), data); |
331 |
| - return |
332 |
| - end |
333 |
| - testCase.assertClass(prototype,"struct"); |
334 |
| - if ~isscalar(prototype) |
335 |
| - prototype = prototype(1); |
336 |
| - end |
337 |
| - testCase.assertThat(fieldnames(data),IsSameSetAs(fieldnames(prototype))); |
338 |
| - for name = fieldnames(data).' |
339 |
| - field = name{1}; |
340 |
| - testCase.verifyClass(data.(field),class(prototype.(field))); |
341 |
| - if isstruct(data.(field)) |
342 |
| - testCase.verifyCompatibleStructs(data.(field),prototype.(field)); |
343 |
| - end |
344 |
| - end |
345 |
| - end |
346 |
| - end |
347 |
| -end |
348 |
| - |
349 |
| -function str = iGetSchema() |
350 |
| -% an example from https://platform.openai.com/docs/guides/structured-outputs/supported-schemas |
351 |
| -str = string(join({ |
352 |
| - '{' |
353 |
| - ' "type": "object",' |
354 |
| - ' "properties": {' |
355 |
| - ' "item": {' |
356 |
| - ' "anyOf": [' |
357 |
| - ' {' |
358 |
| - ' "type": "object",' |
359 |
| - ' "description": "The user object to insert into the database",' |
360 |
| - ' "properties": {' |
361 |
| - ' "name": {' |
362 |
| - ' "type": "string",' |
363 |
| - ' "description": "The name of the user"' |
364 |
| - ' },' |
365 |
| - ' "age": {' |
366 |
| - ' "type": "number",' |
367 |
| - ' "description": "The age of the user"' |
368 |
| - ' }' |
369 |
| - ' },' |
370 |
| - ' "additionalProperties": false,' |
371 |
| - ' "required": [' |
372 |
| - ' "name",' |
373 |
| - ' "age"' |
374 |
| - ' ]' |
375 |
| - ' },' |
376 |
| - ' {' |
377 |
| - ' "type": "object",' |
378 |
| - ' "description": "The address object to insert into the database",' |
379 |
| - ' "properties": {' |
380 |
| - ' "number": {' |
381 |
| - ' "type": "string",' |
382 |
| - ' "description": "The number of the address. Eg. for 123 main st, this would be 123"' |
383 |
| - ' },' |
384 |
| - ' "street": {' |
385 |
| - ' "type": "string",' |
386 |
| - ' "description": "The street name. Eg. for 123 main st, this would be main st"' |
387 |
| - ' },' |
388 |
| - ' "city": {' |
389 |
| - ' "type": "string",' |
390 |
| - ' "description": "The city of the address"' |
391 |
| - ' }' |
392 |
| - ' },' |
393 |
| - ' "additionalProperties": false,' |
394 |
| - ' "required": [' |
395 |
| - ' "number",' |
396 |
| - ' "street",' |
397 |
| - ' "city"' |
398 |
| - ' ]' |
399 |
| - ' }' |
400 |
| - ' ]' |
401 |
| - ' }' |
402 |
| - ' },' |
403 |
| - ' "additionalProperties": false,' |
404 |
| - ' "required": [' |
405 |
| - ' "item"' |
406 |
| - ' ]' |
407 |
| - '}' |
408 |
| -}, newline)); |
409 | 262 | end
|
0 commit comments