Skip to content

Commit

Permalink
Support tool calling for ollamaChat
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
ccreutzi committed Jan 30, 2025
1 parent 098c830 commit b5121ec
Show file tree
Hide file tree
Showing 41 changed files with 816 additions and 261 deletions.
24 changes: 19 additions & 5 deletions +llms/+internal/callOllamaChatAPI.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function [text, message, response] = callOllamaChatAPI(model, messages, nvp)
function [text, message, response] = callOllamaChatAPI(model, messages, functions, nvp)
% This function is undocumented and will change in a future release

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

% Copyright 2023-2024 The MathWorks, Inc.
% Copyright 2023-2025 The MathWorks, Inc.

arguments
model
messages
functions
nvp.ToolChoice
nvp.Temperature
nvp.TopP
nvp.MinP
Expand All @@ -52,7 +54,7 @@
nvp.StopSequences = [nvp.StopSequences, nvp.StopSequences];
end

parameters = buildParametersCall(model, messages, nvp);
parameters = buildParametersCall(model, messages, functions, nvp);

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

Expand All @@ -61,7 +63,11 @@
if response.StatusCode=="OK"
% Outputs the first generation
if isempty(nvp.StreamFun)
message = response.Body.Data.message;
if iscell(response.Body.Data)
message = response.Body.Data{1}.message;
else
message = response.Body.Data.message;
end
else
message = struct("role", "assistant", ...
"content", streamedText);
Expand All @@ -73,7 +79,7 @@
end
end

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

Expand All @@ -83,6 +89,14 @@

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

if ~isempty(functions)
parameters.tools = functions;
end

if ~isempty(nvp.ToolChoice)
parameters.tool_choice = nvp.ToolChoice;
end

options = struct;

if strcmp(nvp.ResponseFormat,"json")
Expand Down
12 changes: 11 additions & 1 deletion +llms/+internal/sendRequest.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
% api key TOKEN. TIMEOUT is the number of seconds to wait for initial
% server connection. STREAMFUN is an optional callback function.

% Copyright 2023-2024 The MathWorks, Inc.
% Copyright 2023-2025 The MathWorks, Inc.

arguments
parameters
Expand Down Expand Up @@ -42,4 +42,14 @@
response = send(request, matlab.net.URI(endpoint),httpOpts,consumer);
streamedText = consumer.ResponseText;
end

% When the server sends jsonl or ndjson back, we do not get the automatic conversion.
if isnumeric(response.Body.Data)
txt = native2unicode(response.Body.Data.',"UTF-8");
% convert to JSON array
json = "[" + replace(strtrim(txt),newline,',') + "]";
try
response.Body.Data = jsondecode(json);
end
end
end
8 changes: 7 additions & 1 deletion +llms/+stream/responseStreamer.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
%responseStreamer Responsible for obtaining the streaming results from the
%API

% Copyright 2023 The MathWorks, Inc.
% Copyright 2023-2025 The MathWorks, Inc.

properties
ResponseText
Expand Down Expand Up @@ -97,6 +97,12 @@
this.StreamFun(txt);
this.ResponseText = [this.ResponseText txt];
end
if isfield(json.message,"tool_calls")
s = json.message.tool_calls;
txt = jsonencode(s);
this.StreamFun('');
this.ResponseText = [this.ResponseText txt];
end
if isfield(json,"done")
stop = json.done;
end
Expand Down
5 changes: 3 additions & 2 deletions +llms/+utils/errorMessageCatalog.m
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
classdef errorMessageCatalog
%errorMessageCatalog Stores the error messages from this repository

% Copyright 2023-2024 The MathWorks, Inc.
% Copyright 2023-2025 The MathWorks, Inc.

properties(Constant)
%CATALOG dictionary mapping error ids to error msgs
Expand Down Expand Up @@ -49,7 +49,8 @@
catalog("llms:mustBeAssistantWithContent") = "Input struct must contain field 'content' containing text with one or more characters.";
catalog("llms:mustBeAssistantWithIdAndFunction") = "Field 'tool_call' must be a struct with fields 'id' and 'function'.";
catalog("llms:mustBeAssistantWithNameAndArguments") = "Field 'function' must be a struct with fields 'name' and 'arguments'.";
catalog("llms:assistantMustHaveTextNameAndArguments") = "Fields 'name' and 'arguments' must be text with one or more characters.";
catalog("llms:assistantMustHaveTextName") = "Field 'name' must be text with one or more characters.";
catalog("llms:assistantMustHaveTextOrStructArguments") = "Field 'arguments' must be text with one or more characters, or a scalar struct.";
catalog("llms:mustBeValidIndex") = "Index exceeds the number of array elements. Index must be less than or equal to {1}.";
catalog("llms:removeFromEmptyHistory") = "Unable to remove message from empty message history.";
catalog("llms:stopSequencesMustHaveMax4Elements") = "Number of stop sequences must be less than or equal to 4.";
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- name: Pull models
run: |
ollama pull mistral
ollama pull mistral-nemo
ollama pull moondream
OLLAMA_HOST=127.0.0.1:11435 ollama pull qwen2:0.5b
- name: Set up MATLAB
Expand Down
4 changes: 2 additions & 2 deletions azureChat.m
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,13 @@
% FunctionNames - Names of the functions that the model can
% request calls.
%
% ResponseFormat - The format of response the model returns.
% ResponseFormat - The format of response the model returns.
% "text" (default) | "json" | struct | string with JSON Schema
%
% TimeOut - Connection Timeout in seconds.
%

% Copyright 2023-2024 The MathWorks, Inc.
% Copyright 2023-2025 The MathWorks, Inc.

properties(SetAccess=private)
Endpoint (1,1) string
Expand Down
3 changes: 2 additions & 1 deletion doc/functions/addToolMessage.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ Updated message history, specified as a [`messageHistory`](messageHistory.md) ob

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

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

*Copyright 2024 The MathWorks, Inc.*

2 changes: 1 addition & 1 deletion doc/functions/generate.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ The supported name\-value arguments depend on the chat completion API.
| `PresencePenalty` | Supported | Supported | |
| `FrequencyPenalty` | Supported | Supported | |
| `NumCompletions` | Supported | Supported | |
| `ToolChoice` | Supported | Supported | |
| `ToolChoice` | Supported | Supported | Supported |
| `MinP` | | | Supported |
| `TopK` | | | Supported |
| `TailFreeSamplingZ` | | | Supported |
Expand Down
24 changes: 22 additions & 2 deletions doc/functions/ollamaChat.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ Specify a custom streaming function to process the generated output as it is gen

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

### `ToolChoice` — Functions to call during output generation

`openAIFunction` object | array of `openAIFunction` objects


Information about tools available for function calling, specified as [`openAIFunction`](openAIFunction.md) objects.


For an example, see [Analyze Text Data Using Parallel Function Calls with Ollama](../../examples/AnalyzeTextDataUsingParallelFunctionCallwithOllama.md).

# Properties Settable at Construction

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.
Expand Down Expand Up @@ -187,9 +197,18 @@ The system prompt is a natural\-language description that provides the framework

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


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

### `FunctionNames` — Names of OpenAI functions to call during output generation

string array


This property is read\-only.


Names of the custom functions specified in the `ToolChoice` name\-value argument.

### `Model` — Model name

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

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

*Copyright 2024 The MathWorks, Inc.*

7 changes: 5 additions & 2 deletions doc/functions/openAIFunction.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

# openAIFunction

Use OpenAI® Function Calls from MATLAB®
Use Function Calls from MATLAB®

# Creation
## Syntax
Expand All @@ -12,6 +12,8 @@ Use OpenAI® Function Calls from MATLAB®

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.

Use `openAIFunction` objects to call tools using OpenAI® or Ollama™.


For example:

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

*Copyright 2024 The MathWorks, Inc.*

Loading

0 comments on commit b5121ec

Please sign in to comment.