diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bef6c2e..83e4508 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,8 @@ jobs: products: Text_Analytics_Toolbox cache: true - name: Run tests and generate artifacts + env: + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} uses: matlab-actions/run-tests@v2 with: test-results-junit: test-results/results.xml diff --git a/tests/textractOpenAIEmbeddings.m b/tests/textractOpenAIEmbeddings.m index 1bf33a6..fca5bc8 100644 --- a/tests/textractOpenAIEmbeddings.m +++ b/tests/textractOpenAIEmbeddings.m @@ -16,7 +16,8 @@ function saveEnvVar(testCase) end properties(TestParameter) - InvalidInput = iGetInvalidInput; + InvalidInput = iGetInvalidInput(); + ValidDimensionsModelCombinations = iGetValidDimensionsModelCombinations(); end methods(Test) @@ -31,6 +32,18 @@ function keyNotFound(testCase) testCase.verifyError(@()extractOpenAIEmbeddings("bla"), "llms:keyMustBeSpecified"); end + function validCombinationOfModelAndDimension(testCase, ValidDimensionsModelCombinations) + testCase.verifyWarningFree(@()extractOpenAIEmbeddings("bla", ... + Dimensions=ValidDimensionsModelCombinations.Dimensions,... + ModelName=ValidDimensionsModelCombinations.ModelName, ... + ApiKey="not-real")); + end + + function embedStringWithSuccessfulOpenAICall(testCase) + testCase.verifyWarningFree(@()extractOpenAIEmbeddings("bla", ... + ApiKey=getenv("OPENAI_KEY"))); + end + function invalidCombinationOfModelAndDimension(testCase) testCase.verifyError(@()extractOpenAIEmbeddings("bla", ... Dimensions=10,... @@ -54,7 +67,7 @@ function testInvalidInputs(testCase, InvalidInput) end end -function invalidInput = iGetInvalidInput +function invalidInput = iGetInvalidInput() invalidInput = struct( ... "InvalidEmptyText", struct( ... "Input",{{ "" }},... @@ -117,4 +130,15 @@ function testInvalidInputs(testCase, InvalidInput) "InvalidApiKeySize",struct( ... "Input",{{"bla", "ApiKey" ["abc" "abc"] }},... "Error","MATLAB:validators:mustBeTextScalar")); -end \ No newline at end of file +end + +function validDimensionsModelCombinations = iGetValidDimensionsModelCombinations() +validDimensionsModelCombinations = struct( ... + "CaseTextEmbedding3Small", struct( ... + "Dimensions",10,... + "ModelName", "text-embedding-3-small"), ... + ... + "CaseTextEmbedding3Large", struct( ... + "Dimensions",10,... + "ModelName", "text-embedding-3-large")); +end diff --git a/tests/topenAIChat.m b/tests/topenAIChat.m index c001c3b..904a206 100644 --- a/tests/topenAIChat.m +++ b/tests/topenAIChat.m @@ -16,9 +16,9 @@ function saveEnvVar(testCase) end properties(TestParameter) - InvalidConstructorInput = iGetInvalidConstructorInput; - InvalidGenerateInput = iGetInvalidGenerateInput; - InvalidValuesSetters = iGetInvalidValuesSetters; + InvalidConstructorInput = iGetInvalidConstructorInput(); + InvalidGenerateInput = iGetInvalidGenerateInput(); + InvalidValuesSetters = iGetInvalidValuesSetters(); end methods(Test) @@ -34,11 +34,8 @@ function generateAcceptsMessagesAsInput(testCase) chat = openAIChat(ApiKey="this-is-not-a-real-key"); messages = openAIMessages; messages = addUserMessage(messages, "This should be okay."); - testCase.verifyWarningFree(@()generate(chat,messages)); - end - function constructMdlWithInvalidParameters(testCase) - testCase.verifyError(@()openAIChat(ApiKey="this-is-not-a-real-key", ModelName="gpt-4", ResponseFormat="json"), "llms:invalidOptionAndValueForModel"); + testCase.verifyWarningFree(@()generate(chat,messages)); end function keyNotFound(testCase) @@ -59,6 +56,7 @@ function constructChatWithAllNVP(testCase) chat = openAIChat(systemPrompt, Tools=functions, ModelName=modelName, ... Temperature=temperature, TopProbabilityMass=topP, StopSequences=stop, ApiKey=apiKey,... FrequencyPenalty=frequenceP, PresencePenalty=presenceP, TimeOut=timeout); + testCase.verifyEqual(chat.ModelName, modelName); testCase.verifyEqual(chat.Temperature, temperature); testCase.verifyEqual(chat.TopProbabilityMass, topP); @@ -69,20 +67,29 @@ function constructChatWithAllNVP(testCase) function verySmallTimeOutErrors(testCase) chat = openAIChat(TimeOut=0.0001, ApiKey="false-key"); + testCase.verifyError(@()generate(chat, "hi"), "MATLAB:webservices:Timeout") end function errorsWhenPassingToolChoiceWithEmptyTools(testCase) chat = openAIChat(ApiKey="this-is-not-a-real-key"); + testCase.verifyError(@()generate(chat,"input", ToolChoice="bla"), "llms:mustSetFunctionsForCall"); end function settingToolChoiceWithNone(testCase) functions = openAIFunction("funName"); chat = openAIChat(ApiKey="this-is-not-a-real-key",Tools=functions); + testCase.verifyWarningFree(@()generate(chat,"This is okay","ToolChoice","none")); end + function settingSeedToInteger(testCase) + chat = openAIChat(ApiKey="this-is-not-a-real-key"); + + testCase.verifyWarningFree(@()generate(chat,"This is okay", "Seed", 2)); + end + function invalidInputsConstructor(testCase, InvalidConstructorInput) testCase.verifyError(@()openAIChat(InvalidConstructorInput.Input{:}), InvalidConstructorInput.Error); end @@ -90,6 +97,7 @@ function invalidInputsConstructor(testCase, InvalidConstructorInput) function invalidInputsGenerate(testCase, InvalidGenerateInput) f = openAIFunction("validfunction"); chat = openAIChat(Tools=f, ApiKey="this-is-not-a-real-key"); + testCase.verifyError(@()generate(chat,InvalidGenerateInput.Input{:}), InvalidGenerateInput.Error); end @@ -107,18 +115,56 @@ function invalidGenerateInputforModel(testCase) image_path = "peppers.png"; emptyMessages = openAIMessages; inValidMessages = addUserMessageWithImages(emptyMessages,"What is in the image?",image_path); + testCase.verifyError(@()generate(chat,inValidMessages), "llms:invalidContentTypeForModel") end function noStopSequencesNoMaxNumTokens(testCase) chat = openAIChat(ApiKey="this-is-not-a-real-key"); + testCase.verifyWarningFree(@()generate(chat,"This is okay")); end + function createOpenAIChatWithStreamFunc(testCase) + + function seen = sf(str) + persistent data; + if isempty(data) + data = strings(1, 0); + end + % Append streamed text to an empty string array of length 1 + data = [data, str]; + seen = data; + end + chat = openAIChat(ApiKey=getenv("OPENAI_KEY"), StreamFun=@sf); + + testCase.verifyWarningFree(@()generate(chat, "Hello world.")); + % Checking that persistent data, which is still stored in + % memory, is greater than 1. This would mean that the stream + % function has been called and streamed some text. + testCase.verifyGreaterThan(numel(sf("")), 1); + end + + function warningJSONResponseFormatGPT35(testCase) + chat = @() openAIChat("You are a useful assistant", ... + ApiKey="this-is-not-a-real-key", ... + ResponseFormat="json", ... + ModelName="gpt-3.5-turbo"); + + testCase.verifyWarning(@()chat(), "llms:warningJsonInstruction"); + end + + function createOpenAIChatWithOpenAIKey(testCase) + chat = openAIChat("You are a useful assistant", ... + ApiKey=getenv("OPENAI_KEY")); + + testCase.verifyWarningFree(@()generate(chat, "Hello world.")); + end + end end -function invalidValuesSetters = iGetInvalidValuesSetters +function invalidValuesSetters = iGetInvalidValuesSetters() invalidValuesSetters = struct( ... "InvalidTemperatureType", struct( ... @@ -222,7 +268,7 @@ function noStopSequencesNoMaxNumTokens(testCase) "Error", "MATLAB:notGreaterEqual")); end -function invalidConstructorInput = iGetInvalidConstructorInput +function invalidConstructorInput = iGetInvalidConstructorInput() validFunction = openAIFunction("funName"); invalidConstructorInput = struct( ... "InvalidResponseFormatValue", struct( ... @@ -233,6 +279,10 @@ function noStopSequencesNoMaxNumTokens(testCase) "Input",{{"ResponseFormat", ["text" "text"] }},... "Error", "MATLAB:validation:IncompatibleSize"), ... ... + "InvalidResponseFormatModelCombination", struct( ... + "Input", {{"ApiKey", "this-is-not-a-real-key", "ModelName", "gpt-4", "ResponseFormat", "json"}}, ... + "Error", "llms:invalidOptionAndValueForModel"), ... + ... "InvalidStreamFunType", struct( ... "Input",{{"StreamFun", "2" }},... "Error", "MATLAB:validators:mustBeA"), ... @@ -366,7 +416,7 @@ function noStopSequencesNoMaxNumTokens(testCase) "Error","MATLAB:validators:mustBeTextScalar")); end -function invalidGenerateInput = iGetInvalidGenerateInput +function invalidGenerateInput = iGetInvalidGenerateInput() emptyMessages = openAIMessages; validMessages = addUserMessage(emptyMessages,"Who invented the telephone?"); @@ -409,5 +459,9 @@ function noStopSequencesNoMaxNumTokens(testCase) ... "InvalidToolChoiceSize",struct( ... "Input",{{ validMessages "ToolChoice" ["validfunction", "validfunction"] }},... - "Error","MATLAB:validators:mustBeTextScalar")); + "Error","MATLAB:validators:mustBeTextScalar"),... + ... + "InvalidSeed",struct( ... + "Input",{{ validMessages "Seed" "2" }},... + "Error","MATLAB:validators:mustBeNumericOrLogical")); end \ No newline at end of file diff --git a/tests/topenAIImages.m b/tests/topenAIImages.m index 2b625e1..8eccf3f 100644 --- a/tests/topenAIImages.m +++ b/tests/topenAIImages.m @@ -129,6 +129,18 @@ function invalidInputsVariation(testCase, InvalidVariationInput) mdl = openAIImages(ApiKey="this-is-not-a-real-key"); testCase.verifyError(@()createVariation(mdl,InvalidVariationInput.Input{:}), InvalidVariationInput.Error); end + + function testThatImageIsReturned(testCase) + mdl = openAIImages(ApiKey=getenv("OPENAI_KEY")); + + [images, response] = generate(mdl, ... + "Create a 3D avatar of a whimsical sushi on the beach. " + ... + "He is decorated with various sushi elements and is " + ... + "playfully interacting with the beach environment."); + + testCase.verifySize(images{:}, [1024, 1024, 3]); + testCase.verifyEqual(response.StatusLine.ReasonPhrase, "OK"); + end end end diff --git a/tests/topenAIMessages.m b/tests/topenAIMessages.m index 15b3411..2aea3e4 100644 --- a/tests/topenAIMessages.m +++ b/tests/topenAIMessages.m @@ -4,12 +4,13 @@ % Copyright 2023-2024 The MathWorks, Inc. properties(TestParameter) - InvalidInputsUserPrompt = iGetInvalidInputsUserPrompt; - InvalidInputsUserImagesPrompt = iGetInvalidInputsUserImagesPrompt; - InvalidInputsFunctionPrompt = iGetInvalidFunctionPrompt; - InvalidInputsSystemPrompt = iGetInvalidInputsSystemPrompt; - InvalidInputsResponseMessage = iGetInvalidInputsResponseMessage; - InvalidRemoveMessage = iGetInvalidRemoveMessage; + InvalidInputsUserPrompt = iGetInvalidInputsUserPrompt(); + InvalidInputsUserImagesPrompt = iGetInvalidInputsUserImagesPrompt(); + InvalidInputsFunctionPrompt = iGetInvalidFunctionPrompt(); + InvalidInputsSystemPrompt = iGetInvalidInputsSystemPrompt(); + InvalidInputsResponseMessage = iGetInvalidInputsResponseMessage(); + InvalidRemoveMessage = iGetInvalidRemoveMessage(); + InvalidFuncCallsCases = iGetInvalidFuncCallsCases() ValidTextInput = {"This is okay"; 'this is ok'}; end @@ -92,6 +93,41 @@ function assistantToolCallMessageIsAdded(testCase) testCase.verifyEqual(msgs.Messages{1}.tool_calls{1}, toolCallPrompt.tool_calls); end + function errorsAssistantWithWithoutToolCallId(testCase) + msgs = openAIMessages; + functionName = "functionName"; + args = "{""arg1"": 1, ""arg2"": 2, ""arg3"": ""3""}"; + funCall = struct("name", functionName, "arguments", args); + toolCall = struct("type", "function", "function", funCall); + toolCallPrompt = struct("role", "assistant", "content", "", "tool_calls", []); + % tool_calls is an array of struct in API response + toolCallPrompt.tool_calls = toolCall; + + testCase.verifyError(@()addResponseMessage(msgs, toolCallPrompt), "llms:mustBeAssistantWithIdAndFunction"); + end + + function errorsAssistantWithToolCallsWithoutNameOrArgs(testCase, InvalidFuncCallsCases) + msgs = openAIMessages; + funCall = InvalidFuncCallsCases.FunCallStruct; + toolCall = struct("id", "123", "type", "function", "function", funCall); + toolCallPrompt = struct("role", "assistant", "content", "", "tool_calls", []); + % tool_calls is an array of struct in API response + toolCallPrompt.tool_calls = toolCall; + + testCase.verifyError(@()addResponseMessage(msgs, toolCallPrompt), InvalidFuncCallsCases.Error); + end + + function errorsAssistantWithWithNonTextNameAndArguments(testCase) + msgs = openAIMessages; + funCall = struct("name", 1, "arguments", 2); + toolCall = struct("id", "123", "type", "function", "function", funCall); + toolCallPrompt = struct("role", "assistant", "content", "", "tool_calls", []); + % tool_calls is an array of struct in API response + toolCallPrompt.tool_calls = toolCall; + + testCase.verifyError(@()addResponseMessage(msgs, toolCallPrompt), "llms:assistantMustHaveTextNameAndArguments"); + end + function assistantToolCallMessageWithoutArgsIsAdded(testCase) msgs = openAIMessages; functionName = "functionName"; @@ -178,136 +214,147 @@ function invalidInputsResponsePrompt(testCase, InvalidInputsResponseMessage) end end -function invalidInputsSystemPrompt = iGetInvalidInputsSystemPrompt - invalidInputsSystemPrompt = struct( ... - "NonStringInputName", ... - struct("Input", {{123, "content"}}, ... - "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... - ... - "NonStringInputContent", ... - struct("Input", {{"name", 123}}, ... - "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... - ... - "EmptytName", ... - struct("Input", {{"", "content"}}, ... - "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... - ... - "EmptytContent", ... - struct("Input", {{"name", ""}}, ... - "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... - ... - "NonScalarInputName", ... - struct("Input", {{["name1" "name2"], "content"}}, ... - "Error", "MATLAB:validators:mustBeTextScalar"),... - ... - "NonScalarInputContent", ... - struct("Input", {{"name", ["content1", "content2"]}}, ... - "Error", "MATLAB:validators:mustBeTextScalar")); +function invalidInputsSystemPrompt = iGetInvalidInputsSystemPrompt() +invalidInputsSystemPrompt = struct( ... + "NonStringInputName", ... + struct("Input", {{123, "content"}}, ... + "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... + ... + "NonStringInputContent", ... + struct("Input", {{"name", 123}}, ... + "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... + ... + "EmptytName", ... + struct("Input", {{"", "content"}}, ... + "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... + ... + "EmptytContent", ... + struct("Input", {{"name", ""}}, ... + "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... + ... + "NonScalarInputName", ... + struct("Input", {{["name1" "name2"], "content"}}, ... + "Error", "MATLAB:validators:mustBeTextScalar"),... + ... + "NonScalarInputContent", ... + struct("Input", {{"name", ["content1", "content2"]}}, ... + "Error", "MATLAB:validators:mustBeTextScalar")); +end + +function invalidInputsUserPrompt = iGetInvalidInputsUserPrompt() +invalidInputsUserPrompt = struct( ... + "NonStringInput", ... + struct("Input", {{123}}, ... + "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... + ... + "NonScalarInput", ... + struct("Input", {{["prompt1" "prompt2"]}}, ... + "Error", "MATLAB:validators:mustBeTextScalar"), ... + ... + "EmptyInput", ... + struct("Input", {{""}}, ... + "Error", "MATLAB:validators:mustBeNonzeroLengthText")); end -function invalidInputsUserPrompt = iGetInvalidInputsUserPrompt - invalidInputsUserPrompt = struct( ... - "NonStringInput", ... - struct("Input", {{123}}, ... - "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... - ... - "NonScalarInput", ... - struct("Input", {{["prompt1" "prompt2"]}}, ... - "Error", "MATLAB:validators:mustBeTextScalar"), ... - ... - "EmptyInput", ... - struct("Input", {{""}}, ... - "Error", "MATLAB:validators:mustBeNonzeroLengthText")); +function invalidInputsUserImagesPrompt = iGetInvalidInputsUserImagesPrompt() +invalidInputsUserImagesPrompt = struct( ... + "NonStringInput", ... + struct("Input", {{123, "peppers.png"}}, ... + "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... + ... + "NonScalarInput", ... + struct("Input", {{["prompt1" "prompt2"], "peppers.png"}}, ... + "Error", "MATLAB:validators:mustBeTextScalar"), ... + ... + "EmptyInput", ... + struct("Input", {{"", "peppers.png"}}, ... + "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... + ... + "NonTextImage", ... + struct("Input", {{"prompt", 123}}, ... + "Error", "MATLAB:validators:mustBeNonzeroLengthText"),... + ... + "EmptyImageName", ... + struct("Input", {{"prompt", 123}}, ... + "Error", "MATLAB:validators:mustBeNonzeroLengthText"),... + ... + "InvalidDetail", ... + struct("Input", {{"prompt", "peppers.png", "Detail", "invalid"}}, ... + "Error", "MATLAB:validators:mustBeMember")); end -function invalidInputsUserImagesPrompt = iGetInvalidInputsUserImagesPrompt - invalidInputsUserImagesPrompt = struct( ... - "NonStringInput", ... - struct("Input", {{123, "peppers.png"}}, ... - "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... - ... - "NonScalarInput", ... - struct("Input", {{["prompt1" "prompt2"], "peppers.png"}}, ... - "Error", "MATLAB:validators:mustBeTextScalar"), ... - ... - "EmptyInput", ... - struct("Input", {{"", "peppers.png"}}, ... - "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... - ... - "NonTextImage", ... - struct("Input", {{"prompt", 123}}, ... - "Error", "MATLAB:validators:mustBeNonzeroLengthText"),... - ... - "EmptyImageName", ... - struct("Input", {{"prompt", 123}}, ... - "Error", "MATLAB:validators:mustBeNonzeroLengthText"),... - ... - "InvalidDetail", ... - struct("Input", {{"prompt", "peppers.png", "Detail", "invalid"}}, ... - "Error", "MATLAB:validators:mustBeMember")); +function invalidFunctionPrompt = iGetInvalidFunctionPrompt() +invalidFunctionPrompt = struct( ... + "NonStringInputName", ... + struct("Input", {{"123", 123, "content"}}, ... + "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... + ... + "NonStringInputContent", ... + struct("Input", {{"123", "name", 123}}, ... + "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... + ... + "EmptytName", ... + struct("Input", {{"123", "", "content"}}, ... + "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... + ... + "EmptytContent", ... + struct("Input", {{"123", "name", ""}}, ... + "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... + ... + "NonScalarInputName", ... + struct("Input", {{"123", ["name1" "name2"], "content"}}, ... + "Error", "MATLAB:validators:mustBeTextScalar"),... + ... + "NonScalarInputContent", ... + struct("Input", {{"123","name", ["content1", "content2"]}}, ... + "Error", "MATLAB:validators:mustBeTextScalar")); end -function invalidFunctionPrompt = iGetInvalidFunctionPrompt - invalidFunctionPrompt = struct( ... - "NonStringInputName", ... - struct("Input", {{"123", 123, "content"}}, ... - "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... - ... - "NonStringInputContent", ... - struct("Input", {{"123", "name", 123}}, ... - "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... - ... - "EmptytName", ... - struct("Input", {{"123", "", "content"}}, ... - "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... - ... - "EmptytContent", ... - struct("Input", {{"123", "name", ""}}, ... - "Error", "MATLAB:validators:mustBeNonzeroLengthText"), ... - ... - "NonScalarInputName", ... - struct("Input", {{"123", ["name1" "name2"], "content"}}, ... - "Error", "MATLAB:validators:mustBeTextScalar"),... - ... - "NonScalarInputContent", ... - struct("Input", {{"123","name", ["content1", "content2"]}}, ... - "Error", "MATLAB:validators:mustBeTextScalar")); +function invalidRemoveMessage = iGetInvalidRemoveMessage() +invalidRemoveMessage = struct( ... + "NonInteger", ... + struct("Input", {{0.5}}, ... + "Error", "MATLAB:validators:mustBeInteger"), ... + ... + "NonPositive", ... + struct("Input", {{0}}, ... + "Error", "MATLAB:validators:mustBePositive"), ... + ... + "NonScalarInput", ... + struct("Input", {{[1 2]}}, ... + "Error", "MATLAB:validation:IncompatibleSize")); end -function invalidRemoveMessage = iGetInvalidRemoveMessage - invalidRemoveMessage = struct( ... - "NonInteger", ... - struct("Input", {{0.5}}, ... - "Error", "MATLAB:validators:mustBeInteger"), ... - ... - "NonPositive", ... - struct("Input", {{0}}, ... - "Error", "MATLAB:validators:mustBePositive"), ... - ... - "NonScalarInput", ... - struct("Input", {{[1 2]}}, ... - "Error", "MATLAB:validation:IncompatibleSize")); +function invalidInputsResponseMessage = iGetInvalidInputsResponseMessage() +invalidInputsResponseMessage = struct( ... + "NonStructInput", ... + struct("Input", {{123}},... + "Error", "MATLAB:validation:UnableToConvert"),... + ... + "NonExistentRole", ... + struct("Input", {{struct("role", "123", "content", "123")}},... + "Error", "llms:mustBeAssistantCall"),... + ... + "NonExistentContent", ... + struct("Input", {{struct("role", "assistant")}},... + "Error", "llms:mustBeAssistantCall"),... + ... + "EmptyContent", ... + struct("Input", {{struct("role", "assistant", "content", "")}},... + "Error", "llms:mustBeAssistantWithContent"),... + ... + "NonScalarContent", ... + struct("Input", {{struct("role", "assistant", "content", ["a", "b"])}},... + "Error", "llms:mustBeAssistantWithContent")); end -function invalidInputsResponseMessage = iGetInvalidInputsResponseMessage - invalidInputsResponseMessage = struct( ... - "NonStructInput", ... - struct("Input", {{123}},... - "Error", "MATLAB:validation:UnableToConvert"),... - ... - "NonExistentRole", ... - struct("Input", {{struct("role", "123", "content", "123")}},... - "Error", "llms:mustBeAssistantCall"),... - ... - "NonExistentContent", ... - struct("Input", {{struct("role", "assistant")}},... - "Error", "llms:mustBeAssistantCall"),... - ... - "EmptyContent", ... - struct("Input", {{struct("role", "assistant", "content", "")}},... - "Error", "llms:mustBeAssistantWithContent"),... - ... - "NonScalarContent", ... - struct("Input", {{struct("role", "assistant", "content", ["a", "b"])}},... - "Error", "llms:mustBeAssistantWithContent")); +function invalidFuncCallsCases = iGetInvalidFuncCallsCases() +invalidFuncCallsCases = struct( ... + "NoArguments", ... + struct("FunCallStruct", struct("name", "functionName"),... + "Error", "llms:mustBeAssistantWithNameAndArguments"),... + ... + "NoName", ... + struct("FunCallStruct", struct("arguments", "{""arg1"": 1, ""arg2"": 2, ""arg3"": ""3""}"), ... + "Error", "llms:mustBeAssistantWithNameAndArguments")); end \ No newline at end of file