diff --git a/+llms/+openai/models.m b/+llms/+openai/models.m index 96f9c17..df170da 100644 --- a/+llms/+openai/models.m +++ b/+llms/+openai/models.m @@ -4,6 +4,7 @@ % Copyright 2024 The MathWorks, Inc. models = [... "gpt-4o","gpt-4o-2024-05-13",... + "gpt-4o-mini","gpt-4o-mini-2024-07-18",... "gpt-4-turbo","gpt-4-turbo-2024-04-09",... "gpt-4","gpt-4-0613", ... "gpt-3.5-turbo","gpt-3.5-turbo-0125", ... diff --git a/azureChat.m b/azureChat.m index fccf7da..27bdded 100644 --- a/azureChat.m +++ b/azureChat.m @@ -103,11 +103,11 @@ function this = azureChat(systemPrompt, nvp) arguments systemPrompt {llms.utils.mustBeTextOrEmpty} = [] - nvp.Endpoint {mustBeNonzeroLengthTextScalar} - nvp.Deployment {mustBeNonzeroLengthTextScalar} + nvp.Endpoint (1,1) string {mustBeNonzeroLengthTextScalar} + nvp.Deployment (1,1) string {mustBeNonzeroLengthTextScalar} nvp.APIKey {mustBeNonzeroLengthTextScalar} nvp.Tools (1,:) {mustBeA(nvp.Tools, "openAIFunction")} = openAIFunction.empty - nvp.APIVersion (1,1) {mustBeAPIVersion} = "2024-02-01" + nvp.APIVersion (1,1) string {mustBeAPIVersion} = "2024-02-01" nvp.Temperature {llms.utils.mustBeValidTemperature} = 1 nvp.TopP {llms.utils.mustBeValidTopP} = 1 nvp.StopSequences {llms.utils.mustBeValidStop} = {} diff --git a/doc/OpenAI.md b/doc/OpenAI.md index 76bd834..51783ae 100644 --- a/doc/OpenAI.md +++ b/doc/OpenAI.md @@ -5,6 +5,7 @@ Several functions in this repository connect MATLAB to the [OpenAIā„¢ Chat Compl To start using the OpenAI APIs, you first need to obtain OpenAI API keys. You are responsible for any fees OpenAI may charge for the use of their APIs. You should be familiar with the limitations and risks associated with using this technology, and you agree that you shall be solely responsible for full compliance with any terms that may apply to your use of the OpenAI APIs. Some of the current LLMs supported on OpenAI are: +- gpt-4o-mini, gpt-4o-mini-2024-07-18 - gpt-3.5-turbo, gpt-3.5-turbo-1106, gpt-3.5-turbo-0125 - gpt-4o, gpt-4o-2024-05-13 (GPT-4 Omni) - gpt-4-turbo, gpt-4-turbo-2024-04-09 (GPT-4 Turbo with Vision) diff --git a/examples/ProcessGeneratedTextInRealTimeByUsingOllamaInStreamingMode.mlx b/examples/ProcessGeneratedTextInRealTimeByUsingOllamaInStreamingMode.mlx index 6725f6e..176a604 100644 Binary files a/examples/ProcessGeneratedTextInRealTimeByUsingOllamaInStreamingMode.mlx and b/examples/ProcessGeneratedTextInRealTimeByUsingOllamaInStreamingMode.mlx differ diff --git a/examples/RetrievalAugmentedGenerationUsingChatGPTandMATLAB.mlx b/examples/RetrievalAugmentedGenerationUsingChatGPTandMATLAB.mlx index d984df4..9c6d61e 100644 Binary files a/examples/RetrievalAugmentedGenerationUsingChatGPTandMATLAB.mlx and b/examples/RetrievalAugmentedGenerationUsingChatGPTandMATLAB.mlx differ diff --git a/examples/RetrievalAugmentedGenerationUsingOllamaAndMATLAB.mlx b/examples/RetrievalAugmentedGenerationUsingOllamaAndMATLAB.mlx new file mode 100644 index 0000000..c248ba0 Binary files /dev/null and b/examples/RetrievalAugmentedGenerationUsingOllamaAndMATLAB.mlx differ diff --git a/openAIChat.m b/openAIChat.m index 6a46cce..7d73c75 100644 --- a/openAIChat.m +++ b/openAIChat.m @@ -11,7 +11,7 @@ % using one or more name-value arguments: % % ModelName - Name of the model to use for chat completions. -% The default value is "gpt-3.5-turbo". +% The default value is "gpt-4o-mini". % % Temperature - Temperature value for controlling the randomness % of the output. Default value is 1; higher values @@ -92,7 +92,7 @@ arguments systemPrompt {llms.utils.mustBeTextOrEmpty} = [] nvp.Tools (1,:) {mustBeA(nvp.Tools, "openAIFunction")} = openAIFunction.empty - nvp.ModelName (1,1) string {mustBeModel} = "gpt-3.5-turbo" + nvp.ModelName (1,1) string {mustBeModel} = "gpt-4o-mini" nvp.Temperature {llms.utils.mustBeValidTemperature} = 1 nvp.TopP {llms.utils.mustBeValidTopP} = 1 nvp.StopSequences {llms.utils.mustBeValidStop} = {} diff --git a/tests/tazureChat.m b/tests/tazureChat.m index 333b1c7..531936f 100644 --- a/tests/tazureChat.m +++ b/tests/tazureChat.m @@ -8,6 +8,7 @@ InvalidGenerateInput = iGetInvalidGenerateInput; InvalidValuesSetters = iGetInvalidValuesSetters; StringInputs = struct('string',{"hi"},'char',{'hi'},'cellstr',{{'hi'}}); + APIVersions = iGetAPIVersions(); end methods(Test) @@ -40,6 +41,14 @@ function doGenerate(testCase,StringInputs) testCase.verifyGreaterThan(strlength(response),0); end + function doGenerateUsingSystemPrompt(testCase) + testCase.assumeTrue(isenv("AZURE_OPENAI_API_KEY"),"end-to-end test requires environment variables AZURE_OPENAI_API_KEY, AZURE_OPENAI_ENDPOINT, and AZURE_OPENAI_DEPLOYMENT."); + chat = azureChat("You are a helpful assistant"); + response = testCase.verifyWarningFree(@() generate(chat,"Hi")); + testCase.verifyClass(response,'string'); + testCase.verifyGreaterThan(strlength(response),0); + end + function generateMultipleResponses(testCase) chat = azureChat; [~,~,response] = generate(chat,"What is a cat?",NumCompletions=3); @@ -150,6 +159,38 @@ function keyNotFound(testCase) unsetenv("AZURE_OPENAI_API_KEY"); testCase.verifyError(@()azureChat, "llms:keyMustBeSpecified"); end + + function canUseAPIVersions(testCase, APIVersions) + % Test that we can use different APIVersion value to call + % azureChat.generate + + testCase.assumeTrue(isenv("AZURE_OPENAI_API_KEY"),"end-to-end test requires environment variables AZURE_OPENAI_API_KEY, AZURE_OPENAI_ENDPOINT, and AZURE_OPENAI_DEPLOYMENT."); + chat = azureChat("APIVersion", APIVersions); + + response = testCase.verifyWarningFree(@() generate(chat,"How similar is the DNA of a cat and a tiger?")); + testCase.verifyClass(response,'string'); + testCase.verifyGreaterThan(strlength(response),0); + end + + function endpointNotFound(testCase) + % to verify the error, we need to unset the environment variable + % AZURE_OPENAI_ENDPOINT, if given. Use a fixture to restore the + % value on leaving the test point + import matlab.unittest.fixtures.EnvironmentVariableFixture + testCase.applyFixture(EnvironmentVariableFixture("AZURE_OPENAI_ENDPOINT","dummy")); + unsetenv("AZURE_OPENAI_ENDPOINT"); + testCase.verifyError(@()azureChat, "llms:endpointMustBeSpecified"); + end + + function deploymentNotFound(testCase) + % to verify the error, we need to unset the environment variable + % AZURE_OPENAI_DEPLOYMENT, if given. Use a fixture to restore the + % value on leaving the test point + import matlab.unittest.fixtures.EnvironmentVariableFixture + testCase.applyFixture(EnvironmentVariableFixture("AZURE_OPENAI_DEPLOYMENT","dummy")); + unsetenv("AZURE_OPENAI_DEPLOYMENT"); + testCase.verifyError(@()azureChat, "llms:deploymentMustBeSpecified"); + end end end @@ -446,3 +487,7 @@ function keyNotFound(testCase) "Input",{{ validMessages "ToolChoice" ["validfunction", "validfunction"] }},... "Error","MATLAB:validators:mustBeTextScalar")); end + +function apiVersions = iGetAPIVersions() +apiVersions = cellstr(llms.azure.apiVersions); +end \ No newline at end of file diff --git a/tests/texampleTests.m b/tests/texampleTests.m index de184d3..a9864d1 100644 --- a/tests/texampleTests.m +++ b/tests/texampleTests.m @@ -105,6 +105,10 @@ function testRetrievalAugmentedGenerationUsingChatGPTandMATLAB(~) RetrievalAugmentedGenerationUsingChatGPTandMATLAB; end + function testRetrievalAugmentedGenerationUsingOllamaAndMATLAB(~) + RetrievalAugmentedGenerationUsingOllamaAndMATLAB; + end + function testSummarizeLargeDocumentsUsingChatGPTandMATLAB(~) SummarizeLargeDocumentsUsingChatGPTandMATLAB; end diff --git a/tests/tollamaChat.m b/tests/tollamaChat.m index bdbadff..9ae230b 100644 --- a/tests/tollamaChat.m +++ b/tests/tollamaChat.m @@ -39,7 +39,19 @@ function doGenerate(testCase,StringInputs) testCase.verifyGreaterThan(strlength(response),0); end + function doGenerateUsingSystemPrompt(testCase) + chat = ollamaChat("mistral","You are a helpful assistant"); + response = testCase.verifyWarningFree(@() generate(chat,"Hi")); + testCase.verifyClass(response,'string'); + testCase.verifyGreaterThan(strlength(response),0); + end + function extremeTopK(testCase) + %% This should work, and it does on some computers. On others, Ollama + %% receives the parameter, but either Ollama or llama.cpp fails to + %% honor it correctly. + testCase.assumeTrue(false,"disabled due to Ollama/llama.cpp not honoring parameter reliably"); + % setting top-k to k=1 leaves no random choice, % so we expect to get a fixed response. chat = ollamaChat("mistral",TopK=1); diff --git a/tests/topenAIChat.m b/tests/topenAIChat.m index 7112b50..2b2b22c 100644 --- a/tests/topenAIChat.m +++ b/tests/topenAIChat.m @@ -34,7 +34,7 @@ function generateAcceptsMessagesAsInput(testCase) function constructChatWithAllNVP(testCase) functions = openAIFunction("funName"); - modelName = "gpt-3.5-turbo"; + modelName = "gpt-4o-mini"; temperature = 0; topP = 1; stop = ["[END]", "."]; @@ -84,7 +84,9 @@ function settingToolChoiceWithNone(testCase) end function fixedSeedFixesResult(testCase) - chat = openAIChat; + % Seed is "beta" in OpenAI documentation + % and not reliable in gpt-4o-mini at this time. + chat = openAIChat(ModelName="gpt-3.5-turbo"); result1 = generate(chat,"This is okay", "Seed", 2); result2 = generate(chat,"This is okay", "Seed", 2); @@ -227,7 +229,7 @@ function warningJSONResponseFormatGPT35(testCase) chat = @() openAIChat("You are a useful assistant", ... APIKey="this-is-not-a-real-key", ... ResponseFormat="json", ... - ModelName="gpt-3.5-turbo"); + ModelName="gpt-4o-mini"); testCase.verifyWarning(@()chat(), "llms:warningJsonInstruction"); end @@ -378,7 +380,7 @@ function keyNotFound(testCase) "FrequencyPenalty", {0}, ... "TimeOut", {10}, ... "FunctionNames", {[]}, ... - "ModelName", {"gpt-3.5-turbo"}, ... + "ModelName", {"gpt-4o-mini"}, ... "SystemPrompt", {[]}, ... "ResponseFormat", {"text"} ... ) ... @@ -394,7 +396,7 @@ function keyNotFound(testCase) "FrequencyPenalty", {0}, ... "TimeOut", {10}, ... "FunctionNames", {[]}, ... - "ModelName", {"gpt-3.5-turbo"}, ... + "ModelName", {"gpt-4o-mini"}, ... "SystemPrompt", {{struct("role","system","content","system prompt")}}, ... "ResponseFormat", {"text"} ... ) ... @@ -410,7 +412,7 @@ function keyNotFound(testCase) "FrequencyPenalty", {0}, ... "TimeOut", {10}, ... "FunctionNames", {[]}, ... - "ModelName", {"gpt-3.5-turbo"}, ... + "ModelName", {"gpt-4o-mini"}, ... "SystemPrompt", {[]}, ... "ResponseFormat", {"text"} ... ) ... @@ -426,7 +428,7 @@ function keyNotFound(testCase) "FrequencyPenalty", {0}, ... "TimeOut", {10}, ... "FunctionNames", {[]}, ... - "ModelName", {"gpt-3.5-turbo"}, ... + "ModelName", {"gpt-4o-mini"}, ... "SystemPrompt", {[]}, ... "ResponseFormat", {"text"} ... ) ... @@ -442,7 +444,7 @@ function keyNotFound(testCase) "FrequencyPenalty", {0}, ... "TimeOut", {10}, ... "FunctionNames", {[]}, ... - "ModelName", {"gpt-3.5-turbo"}, ... + "ModelName", {"gpt-4o-mini"}, ... "SystemPrompt", {[]}, ... "ResponseFormat", {"text"} ... ) ... @@ -458,7 +460,7 @@ function keyNotFound(testCase) "FrequencyPenalty", {0}, ... "TimeOut", {10}, ... "FunctionNames", {[]}, ... - "ModelName", {"gpt-3.5-turbo"}, ... + "ModelName", {"gpt-4o-mini"}, ... "SystemPrompt", {[]}, ... "ResponseFormat", {"text"} ... ) ... @@ -474,7 +476,7 @@ function keyNotFound(testCase) "FrequencyPenalty", {0.1}, ... "TimeOut", {10}, ... "FunctionNames", {[]}, ... - "ModelName", {"gpt-3.5-turbo"}, ... + "ModelName", {"gpt-4o-mini"}, ... "SystemPrompt", {[]}, ... "ResponseFormat", {"text"} ... ) ... @@ -490,7 +492,7 @@ function keyNotFound(testCase) "FrequencyPenalty", {0}, ... "TimeOut", {0.1}, ... "FunctionNames", {[]}, ... - "ModelName", {"gpt-3.5-turbo"}, ... + "ModelName", {"gpt-4o-mini"}, ... "SystemPrompt", {[]}, ... "ResponseFormat", {"text"} ... ) ... @@ -506,7 +508,7 @@ function keyNotFound(testCase) "FrequencyPenalty", {0}, ... "TimeOut", {10}, ... "FunctionNames", {[]}, ... - "ModelName", {"gpt-3.5-turbo"}, ... + "ModelName", {"gpt-4o-mini"}, ... "SystemPrompt", {[]}, ... "ResponseFormat", {"json"} ... ) ... @@ -566,7 +568,7 @@ function keyNotFound(testCase) "Error","MATLAB:validators:mustBeMember"),... ... "InvalidModelNameSize",struct( ... - "Input",{{ "ModelName", ["gpt-3.5-turbo", "gpt-3.5-turbo"] }},... + "Input",{{ "ModelName", ["gpt-4o-mini", "gpt-4o-mini"] }},... "Error","MATLAB:validation:IncompatibleSize"),... ... "InvalidModelNameOption",struct( ...