Skip to content

Commit b5121ec

Browse files
committed
Support tool calling for ollamaChat
* Switch from Mistral to Mistral NeMo Switch from Mistral to Mistral NeMo as the default model for tests and (tested) examples, to prepare for supporting function calls in `ollamaChat`. * Extract structured output tests * Activate structured output for ollamaChat * Improve error message when trying to use structured output with Ollama < 0.5.0 * extract tool call tests * tool support for Ollama * Adapt messageHistory to support Ollama specifics * response streamer should capture tool calls outside OpenAI's format * add Ollama function calling example to texampleTests * Minimal documentation updates * Split llms:assistantMustHaveTextNameAndArguments in two To make the error messages simpler
1 parent 098c830 commit b5121ec

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+816
-261
lines changed

+llms/+internal/callOllamaChatAPI.m

+19-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
function [text, message, response] = callOllamaChatAPI(model, messages, nvp)
1+
function [text, message, response] = callOllamaChatAPI(model, messages, functions, nvp)
22
% This function is undocumented and will change in a future release
33

44
%callOllamaChatAPI Calls the Ollama™ chat completions API.
@@ -22,11 +22,13 @@
2222
% % Send a request
2323
% [text, message] = llms.internal.callOllamaChatAPI(model, messages)
2424

25-
% Copyright 2023-2024 The MathWorks, Inc.
25+
% Copyright 2023-2025 The MathWorks, Inc.
2626

2727
arguments
2828
model
2929
messages
30+
functions
31+
nvp.ToolChoice
3032
nvp.Temperature
3133
nvp.TopP
3234
nvp.MinP
@@ -52,7 +54,7 @@
5254
nvp.StopSequences = [nvp.StopSequences, nvp.StopSequences];
5355
end
5456

55-
parameters = buildParametersCall(model, messages, nvp);
57+
parameters = buildParametersCall(model, messages, functions, nvp);
5658

5759
[response, streamedText] = llms.internal.sendRequestWrapper(parameters,[],URL,nvp.TimeOut,nvp.StreamFun);
5860

@@ -61,7 +63,11 @@
6163
if response.StatusCode=="OK"
6264
% Outputs the first generation
6365
if isempty(nvp.StreamFun)
64-
message = response.Body.Data.message;
66+
if iscell(response.Body.Data)
67+
message = response.Body.Data{1}.message;
68+
else
69+
message = response.Body.Data.message;
70+
end
6571
else
6672
message = struct("role", "assistant", ...
6773
"content", streamedText);
@@ -73,7 +79,7 @@
7379
end
7480
end
7581

76-
function parameters = buildParametersCall(model, messages, nvp)
82+
function parameters = buildParametersCall(model, messages, functions, nvp)
7783
% Builds a struct in the format that is expected by the API, combining
7884
% MESSAGES, FUNCTIONS and parameters in NVP.
7985

@@ -83,6 +89,14 @@
8389

8490
parameters.stream = ~isempty(nvp.StreamFun);
8591

92+
if ~isempty(functions)
93+
parameters.tools = functions;
94+
end
95+
96+
if ~isempty(nvp.ToolChoice)
97+
parameters.tool_choice = nvp.ToolChoice;
98+
end
99+
86100
options = struct;
87101

88102
if strcmp(nvp.ResponseFormat,"json")

+llms/+internal/sendRequest.m

+11-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
% api key TOKEN. TIMEOUT is the number of seconds to wait for initial
66
% server connection. STREAMFUN is an optional callback function.
77

8-
% Copyright 2023-2024 The MathWorks, Inc.
8+
% Copyright 2023-2025 The MathWorks, Inc.
99

1010
arguments
1111
parameters
@@ -42,4 +42,14 @@
4242
response = send(request, matlab.net.URI(endpoint),httpOpts,consumer);
4343
streamedText = consumer.ResponseText;
4444
end
45+
46+
% When the server sends jsonl or ndjson back, we do not get the automatic conversion.
47+
if isnumeric(response.Body.Data)
48+
txt = native2unicode(response.Body.Data.',"UTF-8");
49+
% convert to JSON array
50+
json = "[" + replace(strtrim(txt),newline,',') + "]";
51+
try
52+
response.Body.Data = jsondecode(json);
53+
end
54+
end
4555
end

+llms/+stream/responseStreamer.m

+7-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
%responseStreamer Responsible for obtaining the streaming results from the
33
%API
44

5-
% Copyright 2023 The MathWorks, Inc.
5+
% Copyright 2023-2025 The MathWorks, Inc.
66

77
properties
88
ResponseText
@@ -97,6 +97,12 @@
9797
this.StreamFun(txt);
9898
this.ResponseText = [this.ResponseText txt];
9999
end
100+
if isfield(json.message,"tool_calls")
101+
s = json.message.tool_calls;
102+
txt = jsonencode(s);
103+
this.StreamFun('');
104+
this.ResponseText = [this.ResponseText txt];
105+
end
100106
if isfield(json,"done")
101107
stop = json.done;
102108
end

+llms/+utils/errorMessageCatalog.m

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
classdef errorMessageCatalog
22
%errorMessageCatalog Stores the error messages from this repository
33

4-
% Copyright 2023-2024 The MathWorks, Inc.
4+
% Copyright 2023-2025 The MathWorks, Inc.
55

66
properties(Constant)
77
%CATALOG dictionary mapping error ids to error msgs
@@ -49,7 +49,8 @@
4949
catalog("llms:mustBeAssistantWithContent") = "Input struct must contain field 'content' containing text with one or more characters.";
5050
catalog("llms:mustBeAssistantWithIdAndFunction") = "Field 'tool_call' must be a struct with fields 'id' and 'function'.";
5151
catalog("llms:mustBeAssistantWithNameAndArguments") = "Field 'function' must be a struct with fields 'name' and 'arguments'.";
52-
catalog("llms:assistantMustHaveTextNameAndArguments") = "Fields 'name' and 'arguments' must be text with one or more characters.";
52+
catalog("llms:assistantMustHaveTextName") = "Field 'name' must be text with one or more characters.";
53+
catalog("llms:assistantMustHaveTextOrStructArguments") = "Field 'arguments' must be text with one or more characters, or a scalar struct.";
5354
catalog("llms:mustBeValidIndex") = "Index exceeds the number of array elements. Index must be less than or equal to {1}.";
5455
catalog("llms:removeFromEmptyHistory") = "Unable to remove message from empty message history.";
5556
catalog("llms:stopSequencesMustHaveMax4Elements") = "Number of stop sequences must be less than or equal to 4.";

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
3030
- name: Pull models
3131
run: |
32-
ollama pull mistral
32+
ollama pull mistral-nemo
3333
ollama pull moondream
3434
OLLAMA_HOST=127.0.0.1:11435 ollama pull qwen2:0.5b
3535
- name: Set up MATLAB

azureChat.m

+2-2
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,13 @@
8585
% FunctionNames - Names of the functions that the model can
8686
% request calls.
8787
%
88-
% ResponseFormat - The format of response the model returns.
88+
% ResponseFormat - The format of response the model returns.
8989
% "text" (default) | "json" | struct | string with JSON Schema
9090
%
9191
% TimeOut - Connection Timeout in seconds.
9292
%
9393

94-
% Copyright 2023-2024 The MathWorks, Inc.
94+
% Copyright 2023-2025 The MathWorks, Inc.
9595

9696
properties(SetAccess=private)
9797
Endpoint (1,1) string

doc/functions/addToolMessage.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,8 @@ Updated message history, specified as a [`messageHistory`](messageHistory.md) ob
158158

159159
[`messageHistory`](messageHistory.md) | [`openAIFunction`](openAIFunction.md) | [`generate`](generate.md) | [`addResponseMessage`](addResponseMessage.md)
160160

161-
- [Analyze Text Data Using Parallel Function Calls with ChatGPT](../../examples/AnalyzeTextDataUsingParallelFunctionCallwithChatGPT.md)
161+
- [Analyze Text Data Using Parallel Function Calls with ChatGPT](../../examples/AnalyzeTextDataUsingParallelFunctionCallwithChatGPT.md)
162+
- [Analyze Text Data Using Parallel Function Calls with Ollama](../../examples/AnalyzeTextDataUsingParallelFunctionCallwithOllama.md)
162163

163164
*Copyright 2024 The MathWorks, Inc.*
164165

doc/functions/generate.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ The supported name\-value arguments depend on the chat completion API.
9999
| `PresencePenalty` | Supported | Supported | |
100100
| `FrequencyPenalty` | Supported | Supported | |
101101
| `NumCompletions` | Supported | Supported | |
102-
| `ToolChoice` | Supported | Supported | |
102+
| `ToolChoice` | Supported | Supported | Supported |
103103
| `MinP` | | | Supported |
104104
| `TopK` | | | Supported |
105105
| `TailFreeSamplingZ` | | | Supported |

doc/functions/ollamaChat.md

+22-2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,16 @@ Specify a custom streaming function to process the generated output as it is gen
6262

6363
**Example:** `@(token) fprint("%s",token)`
6464

65+
### `ToolChoice` — Functions to call during output generation
66+
67+
`openAIFunction` object | array of `openAIFunction` objects
68+
69+
70+
Information about tools available for function calling, specified as [`openAIFunction`](openAIFunction.md) objects.
71+
72+
73+
For an example, see [Analyze Text Data Using Parallel Function Calls with Ollama](../../examples/AnalyzeTextDataUsingParallelFunctionCallwithOllama.md).
74+
6575
# Properties Settable at Construction
6676

6777
Optionally specify these properties at construction using name\-value arguments. Specify `PropertyName1=PropertyValue1,...,PropertyNameN=PropertyValueN`, where `PropertyName` is the property name and `PropertyValue` is the corresponding value.
@@ -187,9 +197,18 @@ The system prompt is a natural\-language description that provides the framework
187197

188198
Set the `SystemPrompt` property during construction using the `systemPrompt` input argument.
189199

190-
191200
**Example**: `"You are a helpful assistant who provides answers to user queries in iambic pentameter."`
192201

202+
### `FunctionNames` — Names of OpenAI functions to call during output generation
203+
204+
string array
205+
206+
207+
This property is read\-only.
208+
209+
210+
Names of the custom functions specified in the `ToolChoice` name\-value argument.
211+
193212
### `Model` — Model name
194213

195214
character vector | string scalar
@@ -238,7 +257,8 @@ ans = " This question is attributed to the poem "The Raven" by Edgar Allan Poe.
238257

239258
- [Create Simple Ollama Chat Bot](../../examples/CreateSimpleOllamaChatBot.md)
240259
- [Retrieval Augmented Generation Using Ollama and MATLAB](../../examples/RetrievalAugmentedGenerationusingOllamaAndMATLAB.md)
241-
- [Process Generated Text in Real Time by Using Ollama in Streaming Mode](../../examples/ProcessGeneratedTextInRealTimeByUsingOllamaInStreamingMode.md)
260+
- [Process Generated Text in Real Time by Using Ollama in Streaming Mode](../../examples/ProcessGeneratedTextInRealTimeByUsingOllamaInStreamingMode.md)
261+
- [Analyze Text Data Using Parallel Function Calls with Ollama](../../examples/AnalyzeTextDataUsingParallelFunctionCallwithOllama.md)
242262

243263
*Copyright 2024 The MathWorks, Inc.*
244264

doc/functions/openAIFunction.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
# openAIFunction
33

4-
Use OpenAI® Function Calls from MATLAB®
4+
Use Function Calls from MATLAB®
55

66
# Creation
77
## Syntax
@@ -12,6 +12,8 @@ Use OpenAI® Function Calls from MATLAB®
1212

1313
An `openAIFunction` object represents a tool that you have, such as a MATLAB function. It includes information about the name, syntax, and behavior of the tool. If you pass an `openAIFunction` object to a large language model (LLM), then the LLM can suggest calls to the tool in its generated output. The LLM does not execute the tool itself. However, you can write scripts that automate the tool calls suggested by the LLM.
1414

15+
Use `openAIFunction` objects to call tools using OpenAI® or Ollama™.
16+
1517

1618
For example:
1719

@@ -188,7 +190,8 @@ generatedText = "The sine of thirty degrees is 0.5."
188190
```
189191
# See Also
190192
- [Analyze Scientific Papers Using ChatGPT Function Calls](../../examples/AnalyzeScientificPapersUsingFunctionCalls.md)
191-
- [Analyze Text Data Using Parallel Function Calls with ChatGPT](../../examples/AnalyzeTextDataUsingParallelFunctionCallwithChatGPT.md)
193+
- [Analyze Text Data Using Parallel Function Calls with ChatGPT](../../examples/AnalyzeTextDataUsingParallelFunctionCallwithChatGPT.md)
194+
- [Analyze Text Data Using Parallel Function Calls with Ollama](../../examples/AnalyzeTextDataUsingParallelFunctionCallwithOllama.md)
192195

193196
*Copyright 2024 The MathWorks, Inc.*
194197

0 commit comments

Comments
 (0)