diff --git a/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatModel.java b/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatModel.java
index e4e3af09e..baeeecd3f 100644
--- a/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatModel.java
+++ b/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/OllamaChatModel.java
@@ -16,21 +16,31 @@
package org.springframework.ai.ollama;
import java.util.Base64;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
-import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.MessageType;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.ToolResponseMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
import org.springframework.ai.chat.metadata.ChatResponseMetadata;
+import org.springframework.ai.chat.model.AbstractToolCallSupport;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.model.Generation;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.ModelOptionsUtils;
+import org.springframework.ai.model.function.FunctionCallbackContext;
import org.springframework.ai.ollama.api.OllamaApi;
+import org.springframework.ai.ollama.api.OllamaApi.ChatRequest;
import org.springframework.ai.ollama.api.OllamaApi.Message.Role;
+import org.springframework.ai.ollama.api.OllamaApi.Message.ToolCall;
+import org.springframework.ai.ollama.api.OllamaApi.Message.ToolCallFunction;
import org.springframework.ai.ollama.api.OllamaOptions;
import org.springframework.ai.ollama.metadata.OllamaUsage;
import org.springframework.util.Assert;
@@ -40,21 +50,18 @@ import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
/**
- * {@link ChatModel} implementation for {@literal Ollama}.
- *
- * Ollama allows developers to run large language models and generate embeddings locally.
- * It supports open-source models available on [Ollama AI
- * Library](https://ollama.ai/library). - Llama 2 (7B parameters, 3.8GB size) - Mistral
- * (7B parameters, 4.1GB size)
- *
- * Please refer to the official Ollama website for the
- * most up-to-date information on available models.
+ * {@link ChatModel} implementation for {@literal Ollama}. Ollama allows developers to run
+ * large language models and generate embeddings locally. It supports open-source models
+ * available on [Ollama AI Library](...). - Llama
+ * 2 (7B parameters, 3.8GB size) - Mistral (7B parameters, 4.1GB size) Please refer to the
+ * official Ollama website for the most up-to-date
+ * information on available models.
*
* @author Christian Tzolov
* @author luocongqiu
- * @since 0.8.0
+ * @since 1.0.0
*/
-public class OllamaChatModel implements ChatModel {
+public class OllamaChatModel extends AbstractToolCallSupport implements ChatModel {
/**
* Low-level Ollama API library.
@@ -71,6 +78,12 @@ public class OllamaChatModel implements ChatModel {
}
public OllamaChatModel(OllamaApi chatApi, OllamaOptions defaultOptions) {
+ this(chatApi, defaultOptions, null);
+ }
+
+ public OllamaChatModel(OllamaApi chatApi, OllamaOptions defaultOptions,
+ FunctionCallbackContext functionCallbackContext) {
+ super(functionCallbackContext);
Assert.notNull(chatApi, "OllamaApi must not be null");
Assert.notNull(defaultOptions, "DefaultOptions must not be null");
this.chatApi = chatApi;
@@ -100,11 +113,32 @@ public class OllamaChatModel implements ChatModel {
OllamaApi.ChatResponse response = this.chatApi.chat(ollamaChatRequest(prompt, false));
- var generator = new Generation(response.message().content());
+ List toolCalls = response.message().toolCalls() == null ? List.of()
+ : response.message()
+ .toolCalls()
+ .stream()
+ .map(toolCall -> new AssistantMessage.ToolCall("", "function", toolCall.function().name(),
+ ModelOptionsUtils.toJsonString(toolCall.function().arguments())))
+ .toList();
+
+ var assistantMessage = new AssistantMessage(response.message().content(), Map.of(), toolCalls);
+
+ ChatGenerationMetadata generationMetadata = ChatGenerationMetadata.NULL;
if (response.promptEvalCount() != null && response.evalCount() != null) {
- generator = generator.withGenerationMetadata(ChatGenerationMetadata.from("unknown", null));
+ generationMetadata = ChatGenerationMetadata.from("DONE", null);
}
- return new ChatResponse(List.of(generator), from(response));
+
+ var generator = new Generation(assistantMessage, generationMetadata);
+ var chatResponse = new ChatResponse(List.of(generator), from(response));
+
+ if (isToolCall(chatResponse, Set.of("DONE"))) {
+ var toolCallConversation = handleToolCalls(prompt, chatResponse);
+ // Recursively call the call method with the tool call message
+ // conversation that contains the call responses.
+ return this.call(new Prompt(toolCallConversation, prompt.getOptions()));
+ }
+
+ return chatResponse;
}
public static ChatResponseMetadata from(OllamaApi.ChatResponse response) {
@@ -126,15 +160,39 @@ public class OllamaChatModel implements ChatModel {
@Override
public Flux stream(Prompt prompt) {
- Flux response = this.chatApi.streamingChat(ollamaChatRequest(prompt, true));
+ Flux ollamaResponse = this.chatApi.streamingChat(ollamaChatRequest(prompt, true));
- return response.map(chunk -> {
- Generation generation = (chunk.message() != null) ? new Generation(chunk.message().content())
- : new Generation("");
- if (Boolean.TRUE.equals(chunk.done())) {
- generation = generation.withGenerationMetadata(ChatGenerationMetadata.from("unknown", null));
+ Flux chatResponse = ollamaResponse.map(chunk -> {
+ String content = (chunk.message() != null) ? chunk.message().content() : "";
+ List toolCalls = chunk.message().toolCalls() == null ? List.of()
+ : chunk.message()
+ .toolCalls()
+ .stream()
+ .map(toolCall -> new AssistantMessage.ToolCall("", "function", toolCall.function().name(),
+ ModelOptionsUtils.toJsonString(toolCall.function().arguments())))
+ .toList();
+
+ var assistantMessage = new AssistantMessage(content, Map.of(), toolCalls);
+
+ ChatGenerationMetadata generationMetadata = ChatGenerationMetadata.NULL;
+ if (chunk.promptEvalCount() != null && chunk.evalCount() != null) {
+ generationMetadata = ChatGenerationMetadata.from("DONE", null);
+ }
+
+ var generator = new Generation(assistantMessage, generationMetadata);
+ return new ChatResponse(List.of(generator), from(chunk));
+ });
+
+ return chatResponse.flatMap(response -> {
+ if (isToolCall(response, Set.of("DONE"))) {
+ var toolCallConversation = handleToolCalls(prompt, response);
+ // Recursively call the stream method with the tool call message
+ // conversation that contains the call responses.
+ return this.stream(new Prompt(toolCallConversation, prompt.getOptions()));
+ }
+ else {
+ return Flux.just(response);
}
- return new ChatResponse(List.of(generation), from(chunk));
});
}
@@ -147,28 +205,61 @@ public class OllamaChatModel implements ChatModel {
.stream()
.filter(message -> message.getMessageType() == MessageType.USER
|| message.getMessageType() == MessageType.ASSISTANT
- || message.getMessageType() == MessageType.SYSTEM)
- .map(m -> {
- var messageBuilder = OllamaApi.Message.builder(toRole(m)).withContent(m.getContent());
- if (m instanceof UserMessage userMessage) {
+ || message.getMessageType() == MessageType.SYSTEM || message.getMessageType() == MessageType.TOOL)
+ .map(message -> {
+ if (message instanceof UserMessage userMessage) {
+ var messageBuilder = OllamaApi.Message.builder(Role.USER).withContent(message.getContent());
if (!CollectionUtils.isEmpty(userMessage.getMedia())) {
messageBuilder.withImages(userMessage.getMedia()
.stream()
.map(media -> this.fromMediaData(media.getData()))
.toList());
}
+ return List.of(messageBuilder.build());
}
- return messageBuilder.build();
+ else if (message instanceof SystemMessage systemMessage) {
+ return List
+ .of(OllamaApi.Message.builder(Role.SYSTEM).withContent(systemMessage.getContent()).build());
+ }
+ else if (message instanceof AssistantMessage assistantMessage) {
+ List toolCalls = null;
+ if (!CollectionUtils.isEmpty(assistantMessage.getToolCalls())) {
+ toolCalls = assistantMessage.getToolCalls().stream().map(toolCall -> {
+ var function = new ToolCallFunction(toolCall.name(),
+ ModelOptionsUtils.jsonToMap(toolCall.arguments()));
+ return new ToolCall(function);
+ }).toList();
+ }
+ return List.of(OllamaApi.Message.builder(Role.ASSISTANT)
+ .withContent(assistantMessage.getContent())
+ .withToolCalls(toolCalls)
+ .build());
+ }
+ else if (message instanceof ToolResponseMessage toolMessage) {
+
+ List responseMessages = toolMessage.getResponses()
+ .stream()
+ .map(tr -> OllamaApi.Message.builder(Role.TOOL).withContent(tr.responseData()).build())
+ .toList();
+
+ return responseMessages;
+ }
+ throw new IllegalArgumentException("Unsupported message type: " + message.getMessageType());
})
+ .flatMap(List::stream)
.toList();
+ Set functionsForThisRequest = new HashSet<>();
+
// runtime options
OllamaOptions runtimeOptions = null;
if (prompt.getOptions() != null) {
runtimeOptions = ModelOptionsUtils.copyToTarget(prompt.getOptions(), ChatOptions.class,
OllamaOptions.class);
+ functionsForThisRequest.addAll(this.handleFunctionCallbackConfigurations(runtimeOptions, IS_RUNTIME_CALL));
}
+ functionsForThisRequest.addAll(this.handleFunctionCallbackConfigurations(this.defaultOptions, IS_RUNTIME_CALL));
OllamaOptions mergedOptions = ModelOptionsUtils.merge(runtimeOptions, this.defaultOptions, OllamaOptions.class);
// Override the model.
@@ -190,6 +281,11 @@ public class OllamaChatModel implements ChatModel {
requestBuilder.withKeepAlive(mergedOptions.getKeepAlive());
}
+ // Add the enabled functions definitions to the request's tools parameter.
+ if (!CollectionUtils.isEmpty(functionsForThisRequest)) {
+ requestBuilder.withTools(this.getFunctionTools(functionsForThisRequest));
+ }
+
return requestBuilder.build();
}
@@ -206,18 +302,12 @@ public class OllamaChatModel implements ChatModel {
}
- private OllamaApi.Message.Role toRole(Message message) {
-
- switch (message.getMessageType()) {
- case USER:
- return Role.USER;
- case ASSISTANT:
- return Role.ASSISTANT;
- case SYSTEM:
- return Role.SYSTEM;
- default:
- throw new IllegalArgumentException("Unsupported message type: " + message.getMessageType());
- }
+ private List getFunctionTools(Set functionNames) {
+ return this.resolveFunctionCallbacks(functionNames).stream().map(functionCallback -> {
+ var function = new ChatRequest.Tool.Function(functionCallback.getName(), functionCallback.getDescription(),
+ functionCallback.getInputTypeSchema());
+ return new ChatRequest.Tool(function);
+ }).toList();
}
@Override
diff --git a/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/api/OllamaApi.java b/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/api/OllamaApi.java
index 0b66d46bd..6e141f24b 100644
--- a/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/api/OllamaApi.java
+++ b/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/api/OllamaApi.java
@@ -23,14 +23,10 @@ import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonInclude.Include;
-import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
+import org.springframework.ai.model.ModelOptionsUtils;
+import org.springframework.boot.context.properties.bind.ConstructorBinding;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
@@ -40,8 +36,15 @@ import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
/**
- * Java Client for the Ollama API. https://ollama.ai/
+ * Java Client for the Ollama API. https://ollama.ai
*
* @author Christian Tzolov
* @since 0.8.0
@@ -124,13 +127,13 @@ public class OllamaApi {
*
* @param model (required) The model to use for completion.
* @param prompt (required) The prompt(s) to generate completions for.
- * @param format (optional) The format to return the response in. Currently the only
+ * @param format (optional) The format to return the response in. Currently, the only
* accepted value is "json".
* @param options (optional) additional model parameters listed in the documentation
- * for the Modelfile such as temperature.
- * @param system (optional) system prompt to (overrides what is defined in the Modelfile).
+ * for the Model file such as temperature.
+ * @param system (optional) system prompt to (overrides what is defined in the Model file).
* @param template (optional) the full prompt or prompt template (overrides what is
- * defined in the Modelfile).
+ * defined in the Model file).
* @param context the context parameter returned from a previous request to /generate,
* this can be used to keep a short conversational memory.
* @param stream (optional) if false the response will be returned as a single
@@ -157,7 +160,7 @@ public class OllamaApi {
@JsonProperty("keep_alive") String keepAlive) {
/**
- * Short cut constructor to create a CompletionRequest without options.
+ * Shortcut constructor to create a CompletionRequest without options.
* @param model The model used for completion.
* @param prompt The prompt(s) to generate completions for.
* @param stream Whether to stream the response.
@@ -167,7 +170,7 @@ public class OllamaApi {
}
/**
- * Short cut constructor to create a CompletionRequest without options.
+ * Shortcut constructor to create a CompletionRequest without options.
* @param model The model used for completion.
* @param prompt The prompt(s) to generate completions for.
* @param enableJsonFormat Whether to return the response in json format.
@@ -356,7 +359,8 @@ public class OllamaApi {
public record Message(
@JsonProperty("role") Role role,
@JsonProperty("content") String content,
- @JsonProperty("images") List images) {
+ @JsonProperty("images") List images,
+ @JsonProperty("tool_calls") List toolCalls) {
/**
* The role of the message in the conversation.
@@ -374,10 +378,36 @@ public class OllamaApi {
/**
* Assistant message type. Usually the response from the model.
*/
- @JsonProperty("assistant") ASSISTANT;
+ @JsonProperty("assistant") ASSISTANT,
+ /**
+ * Tool message.
+ */
+ @JsonProperty("tool") TOOL
}
+ /**
+ * The relevant tool call.
+ *
+ * @param function The function definition.
+ */
+ @JsonInclude(Include.NON_NULL)
+ public record ToolCall(
+ @JsonProperty("function") ToolCallFunction function) {
+ }
+
+ /**
+ * The function definition.
+ *
+ * @param name The name of the function.
+ * @param arguments The arguments that the model expects you to pass to the function.
+ */
+ @JsonInclude(Include.NON_NULL)
+ public record ToolCallFunction(
+ @JsonProperty("name") String name,
+ @JsonProperty("arguments") Map arguments) {
+ }
+
public static Builder builder(Role role) {
return new Builder(role);
}
@@ -387,6 +417,7 @@ public class OllamaApi {
private final Role role;
private String content;
private List images;
+ private List toolCalls;
public Builder(Role role) {
this.role = role;
@@ -402,8 +433,13 @@ public class OllamaApi {
return this;
}
+ public Builder withToolCalls(List toolCalls) {
+ this.toolCalls = toolCalls;
+ return this;
+ }
+
public Message build() {
- return new Message(role, content, images);
+ return new Message(role, content, images, toolCalls);
}
}
@@ -417,7 +453,7 @@ public class OllamaApi {
* @param stream Whether to stream the response.
* @param format The format to return the response in. Currently, the only accepted
* value is "json".
- * @param keepAlive The duration to keep the model loaded in ollama while idle. https://pkg.go.dev/time#ParseDuration
+ * @param keepAlive The duration to keep the model loaded in ollama while idle. {@link https://pkg.go.dev/time#ParseDuration}
* @param options Additional model parameters. You can use the {@link OllamaOptions} builder
* to create the options then {@link OllamaOptions#toMap()} to convert the options into a
* map.
@@ -429,8 +465,68 @@ public class OllamaApi {
@JsonProperty("stream") Boolean stream,
@JsonProperty("format") String format,
@JsonProperty("keep_alive") String keepAlive,
- @JsonProperty("options") Map options) {
+ @JsonProperty("options") Map options,
+ @JsonProperty("tools") List tools) {
+
+ /**
+ * Represents a tool the model may call. Currently, only functions are supported as a tool.
+ *
+ * @param type The type of the tool. Currently, only 'function' is supported.
+ * @param function The function definition.
+ */
+ @JsonInclude(Include.NON_NULL)
+ public record Tool(
+ @JsonProperty("type") Type type,
+ @JsonProperty("function") Function function) {
+
+ /**
+ * Create a tool of type 'function' and the given function definition.
+ * @param function function definition.
+ */
+ @ConstructorBinding
+ public Tool(Function function) {
+ this(Type.FUNCTION, function);
+ }
+
+ /**
+ * Create a tool of type 'function' and the given function definition.
+ */
+ public enum Type {
+ /**
+ * Function tool type.
+ */
+ @JsonProperty("function") FUNCTION
+ }
+
+ /**
+ * Function definition.
+ *
+ * @param name The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes.
+ * @param description A description of what the function does, used by the model to choose when and how to call
+ * the function.
+ * @param parameters The parameters the functions accepts, described as a JSON Schema object. To describe a
+ * function that accepts no parameters, provide the value {"type": "object", "properties": {}}.
+ */
+ public record Function(
+ @JsonProperty("name") String name,
+ @JsonProperty("description") String description,
+ @JsonProperty("parameters") Map parameters) {
+
+ /**
+ * Create tool function definition.
+ *
+ * @param description tool function description.
+ * @param name tool function name.
+ * @param jsonSchema tool function schema as json.
+ */
+ @ConstructorBinding
+ public Function(String description, String name, String jsonSchema) {
+ this(description, name, ModelOptionsUtils.jsonToMap(jsonSchema));
+ }
+ }
+ }
+
public static Builder builder(String model) {
return new Builder(model);
}
@@ -443,6 +539,7 @@ public class OllamaApi {
private String format;
private String keepAlive;
private Map options = Map.of();
+ private List tools = List.of();
public Builder(String model) {
Assert.notNull(model, "The model can not be null.");
@@ -482,8 +579,13 @@ public class OllamaApi {
return this;
}
+ public Builder withTools(List tools) {
+ this.tools = tools;
+ return this;
+ }
+
public ChatRequest build() {
- return new ChatRequest(model, messages, stream, format, keepAlive, options);
+ return new ChatRequest(model, messages, stream, format, keepAlive, options, tools);
}
}
}
@@ -526,7 +628,6 @@ public class OllamaApi {
/**
* Generate the next message in a chat with a provided model.
- *
* This is a streaming endpoint (controlled by the 'stream' request property), so
* there will be a series of responses. The final response object will include
* statistics and additional data from the request.
@@ -577,7 +678,7 @@ public class OllamaApi {
* @param prompt The text to generate embeddings for.
* @param keepAlive Controls how long the model will stay loaded into memory following the request (default: 5m).
* @param options Additional model parameters listed in the documentation for the
- * Modelfile such as temperature.
+ * Model file such as temperature.
*/
@JsonInclude(Include.NON_NULL)
public record EmbeddingRequest(
@@ -587,7 +688,7 @@ public class OllamaApi {
@JsonProperty("options") Map options) {
/**
- * short cut constructor to create a EmbeddingRequest without options.
+ * Shortcut constructor to create a EmbeddingRequest without options.
* @param model The name of model to generate embeddings from.
* @param prompt The text to generate embeddings for.
*/
diff --git a/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/api/OllamaOptions.java b/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/api/OllamaOptions.java
index 5847bc82c..1668a1b90 100644
--- a/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/api/OllamaOptions.java
+++ b/models/spring-ai-ollama/src/main/java/org/springframework/ai/ollama/api/OllamaOptions.java
@@ -15,19 +15,26 @@
*/
package org.springframework.ai.ollama.api;
+import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
import java.util.stream.Collectors;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonInclude.Include;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.embedding.EmbeddingOptions;
+import org.springframework.ai.model.ModelOptionsUtils;
+import org.springframework.ai.model.function.FunctionCallback;
+import org.springframework.ai.model.function.FunctionCallingOptions;
+import org.springframework.boot.context.properties.NestedConfigurationProperty;
+import org.springframework.util.Assert;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Helper class for creating strongly-typed Ollama options.
@@ -40,7 +47,7 @@ import org.springframework.ai.embedding.EmbeddingOptions;
* @see Ollama Types
*/
@JsonInclude(Include.NON_NULL)
-public class OllamaOptions implements ChatOptions, EmbeddingOptions {
+public class OllamaOptions implements FunctionCallingOptions, ChatOptions, EmbeddingOptions {
public static final String DEFAULT_MODEL = OllamaModel.MISTRAL.id();
@@ -71,7 +78,7 @@ public class OllamaOptions implements ChatOptions, EmbeddingOptions {
@JsonProperty("num_gqa") private Integer numGQA;
/**
- * The number of layers to send to the GPU(s). On macOS it defaults to 1
+ * The number of layers to send to the GPU(s). On macOS, it defaults to 1
* to enable metal support, 0 to disable.
*/
@JsonProperty("num_gpu") private Integer numGPU;
@@ -248,6 +255,37 @@ public class OllamaOptions implements ChatOptions, EmbeddingOptions {
*/
@JsonProperty("keep_alive") private String keepAlive;
+ /**
+ * OpenAI Tool Function Callbacks to register with the ChatModel.
+ * For Prompt Options the functionCallbacks are automatically enabled for the duration of the prompt execution.
+ * For Default Options the functionCallbacks are registered but disabled by default. Use the enableFunctions to set the functions
+ * from the registry to be used by the ChatModel chat completion requests.
+ */
+ @NestedConfigurationProperty
+ @JsonIgnore
+ private List functionCallbacks = new ArrayList<>();
+
+ /**
+ * List of functions, identified by their names, to configure for function calling in
+ * the chat completion requests.
+ * Functions with those names must exist in the functionCallbacks registry.
+ * The {@link #functionCallbacks} from the PromptOptions are automatically enabled for the duration of the prompt execution.
+ * Note that function enabled with the default options are enabled for all chat completion requests. This could impact the token count and the billing.
+ * If the functions is set in a prompt options, then the enabled functions are only active for the duration of this prompt execution.
+ */
+ @NestedConfigurationProperty
+ @JsonIgnore
+ private Set functions = new HashSet<>();
+
+
+ public static OllamaOptions builder() {
+ return new OllamaOptions();
+ }
+
+ public OllamaOptions build() {
+ return this;
+ }
+
/**
* @param model The ollama model names to use. See the {@link OllamaModel} for the common models.
*/
@@ -424,6 +462,22 @@ public class OllamaOptions implements ChatOptions, EmbeddingOptions {
return this;
}
+ public OllamaOptions withFunctionCallbacks(List functionCallbacks) {
+ this.functionCallbacks = functionCallbacks;
+ return this;
+ }
+
+ public OllamaOptions withFunctions(Set functions) {
+ this.functions = functions;
+ return this;
+ }
+
+ public OllamaOptions withFunction(String functionName) {
+ Assert.hasText(functionName, "Function name must not be empty");
+ this.functions.add(functionName);
+ return this;
+ }
+
public String getFormat() {
return this.format;
}
@@ -680,19 +734,33 @@ public class OllamaOptions implements ChatOptions, EmbeddingOptions {
this.stop = stop;
}
+ @Override
+ public List getFunctionCallbacks() {
+ return this.functionCallbacks;
+ }
+
+ @Override
+ public void setFunctionCallbacks(List functionCallbacks) {
+ this.functionCallbacks = functionCallbacks;
+
+ }
+
+ @Override
+ public Set getFunctions() {
+ return this.functions;
+ }
+
+ @Override
+ public void setFunctions(Set functions) {
+ this.functions = functions;
+ }
+
/**
* Convert the {@link OllamaOptions} object to a {@link Map} of key/value pairs.
* @return The {@link Map} of key/value pairs.
*/
public Map toMap() {
- try {
- var json = new ObjectMapper().writeValueAsString(this);
- return new ObjectMapper().readValue(json, new TypeReference