From 305bab981900e14ab8cd9f8eedb25bc6825a6b26 Mon Sep 17 00:00:00 2001 From: amithkoujalgi Date: Fri, 26 Sep 2025 22:13:55 +0530 Subject: [PATCH] Refactor OllamaAPI and related classes for improved embedding model support and tool registration This update modifies the OllamaAPI class to enhance support for embedding models by renaming related classes and introducing new request and response models. The OllamaEmbedRequestModel and OllamaEmbedResponseModel classes have been added, along with their corresponding builder class. Additionally, the tool registration process has been improved with the introduction of annotations for automatic tool discovery. Deprecated methods and commented-out code have been removed for clarity, and Javadoc comments have been updated for consistency across the API. --- .../java/io/github/ollama4j/OllamaAPI.java | 490 ++++++------------ .../models/chat/OllamaChatMessage.java | 1 + .../models/chat/OllamaChatMessageRole.java | 2 - .../models/chat/OllamaChatRequestBuilder.java | 7 - .../models/chat/OllamaChatResult.java | 15 - .../models/chat/OllamaChatStreamObserver.java | 4 - .../OllamaEmbedRequestBuilder.java | 2 +- .../OllamaEmbedRequestModel.java | 3 +- .../OllamaEmbedResponseModel.java | 2 +- .../OllamaGenerateStreamObserver.java | 4 - .../models/ps/ModelsProcessResponse.java | 2 +- .../models/request/CustomModelRequest.java | 2 +- .../request/OllamaChatEndpointCaller.java | 8 +- .../models/request/OllamaEndpointCaller.java | 2 +- .../request/OllamaGenerateEndpointCaller.java | 1 - .../response/OllamaAsyncResultStreamer.java | 2 - .../java/io/github/ollama4j/tools/Tools.java | 2 + .../tools/annotations/OllamaToolService.java | 15 +- .../ollama4j/tools/annotations/ToolSpec.java | 19 +- .../io/github/ollama4j/utils/Options.java | 2 + .../ollama4j/unittests/TestMockedAPIs.java | 4 +- .../TestEmbedRequestSerialization.java | 4 +- 22 files changed, 202 insertions(+), 391 deletions(-) rename src/main/java/io/github/ollama4j/models/{embeddings => embed}/OllamaEmbedRequestBuilder.java (96%) rename src/main/java/io/github/ollama4j/models/{embeddings => embed}/OllamaEmbedRequestModel.java (93%) rename src/main/java/io/github/ollama4j/models/{embeddings => embed}/OllamaEmbedResponseModel.java (94%) diff --git a/src/main/java/io/github/ollama4j/OllamaAPI.java b/src/main/java/io/github/ollama4j/OllamaAPI.java index d8da8ed..ef0b843 100644 --- a/src/main/java/io/github/ollama4j/OllamaAPI.java +++ b/src/main/java/io/github/ollama4j/OllamaAPI.java @@ -15,8 +15,8 @@ import io.github.ollama4j.exceptions.ToolInvocationException; import io.github.ollama4j.metrics.MetricsRecorder; import io.github.ollama4j.models.chat.*; import io.github.ollama4j.models.chat.OllamaChatTokenHandler; -import io.github.ollama4j.models.embeddings.OllamaEmbedRequestModel; -import io.github.ollama4j.models.embeddings.OllamaEmbedResponseModel; +import io.github.ollama4j.models.embed.OllamaEmbedRequestModel; +import io.github.ollama4j.models.embed.OllamaEmbedResponseModel; import io.github.ollama4j.models.generate.OllamaGenerateRequest; import io.github.ollama4j.models.generate.OllamaGenerateStreamObserver; import io.github.ollama4j.models.generate.OllamaGenerateTokenHandler; @@ -24,9 +24,15 @@ import io.github.ollama4j.models.ps.ModelsProcessResponse; import io.github.ollama4j.models.request.*; import io.github.ollama4j.models.response.*; import io.github.ollama4j.tools.*; +import io.github.ollama4j.tools.annotations.OllamaToolService; +import io.github.ollama4j.tools.annotations.ToolProperty; +import io.github.ollama4j.tools.annotations.ToolSpec; import io.github.ollama4j.utils.Constants; import io.github.ollama4j.utils.Utils; import java.io.*; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; import java.net.URI; import java.net.URISyntaxException; import java.net.http.HttpClient; @@ -42,10 +48,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * The base Ollama API class for interacting with the Ollama server. + * The main API class for interacting with the Ollama server. * - *

This class provides methods for model management, chat, embeddings, tool registration, and - * more. + *

This class provides methods for model management, chat, embeddings, tool registration, and more. */ @SuppressWarnings({"DuplicatedCode", "resource", "SpellCheckingInspection"}) public class OllamaAPI { @@ -59,8 +64,8 @@ public class OllamaAPI { /** * The request timeout in seconds for API calls. - * - *

Default is 10 seconds. This value determines how long the client will wait for a response + *

+ * Default is 10 seconds. This value determines how long the client will wait for a response * from the Ollama server before timing out. */ @Setter private long requestTimeoutSeconds = 10; @@ -73,19 +78,19 @@ public class OllamaAPI { /** * The maximum number of retries for tool calls during chat interactions. - * - *

This value controls how many times the API will attempt to call a tool in the event of a + *

+ * This value controls how many times the API will attempt to call a tool in the event of a * failure. Default is 3. */ @Setter private int maxChatToolCallRetries = 3; /** * The number of retries to attempt when pulling a model from the Ollama server. - * - *

If set to 0, no retries will be performed. If greater than 0, the API will retry pulling + *

+ * If set to 0, no retries will be performed. If greater than 0, the API will retry pulling * the model up to the specified number of times in case of failure. - * - *

Default is 0 (no retries). + *

+ * Default is 0 (no retries). */ @Setter @SuppressWarnings({"FieldMayBeFinal", "FieldCanBeLocal"}) @@ -93,13 +98,15 @@ public class OllamaAPI { /** * Enable or disable Prometheus metrics collection. - * - *

When enabled, the API will collect and expose metrics for request counts, durations, model + *

+ * When enabled, the API will collect and expose metrics for request counts, durations, model * usage, and other operational statistics. Default is false. */ @Setter private boolean metricsEnabled = false; - /** Instantiates the Ollama API with the default Ollama host: {@code http://localhost:11434} */ + /** + * Instantiates the Ollama API with the default Ollama host: {@code http://localhost:11434} + */ public OllamaAPI() { this.host = "http://localhost:11434"; // initializeMetrics(); @@ -121,8 +128,7 @@ public class OllamaAPI { } /** - * Set basic authentication for accessing an Ollama server that's behind a - * reverse-proxy/gateway. + * Set basic authentication for accessing an Ollama server that's behind a reverse-proxy/gateway. * * @param username the username * @param password the password @@ -132,8 +138,7 @@ public class OllamaAPI { } /** - * Set Bearer authentication for accessing an Ollama server that's behind a - * reverse-proxy/gateway. + * Set Bearer authentication for accessing an Ollama server that's behind a reverse-proxy/gateway. * * @param bearerToken the Bearer authentication token to provide */ @@ -357,8 +362,8 @@ public class OllamaAPI { } /** - * Processes a single ModelPullResponse, handling errors and logging status. Returns true if the - * response indicates a successful pull. + * Processes a single ModelPullResponse, handling errors and logging status. + * Returns true if the response indicates a successful pull. * * @param modelPullResponse the response from the model pull * @param modelName the name of the model @@ -429,9 +434,9 @@ public class OllamaAPI { } /** - * Pulls a model using the specified Ollama library model tag. The model is identified by a name - * and a tag, which are combined into a single identifier in the format "name:tag" to pull the - * corresponding model. + * Pulls a model using the specified Ollama library model tag. + * The model is identified by a name and a tag, which are combined into a single identifier + * in the format "name:tag" to pull the corresponding model. * * @param modelName the name/tag of the model to be pulled. Ex: llama3:latest * @throws OllamaBaseException if the response indicates an error status @@ -511,8 +516,8 @@ public class OllamaAPI { } /** - * Creates a custom model. Read more about custom model creation here. + * Creates a custom model. Read more about custom model creation + * here. * * @param customModelRequest custom model spec * @throws OllamaBaseException if the response indicates an error status @@ -575,8 +580,7 @@ public class OllamaAPI { * Deletes a model from the Ollama server. * * @param modelName the name of the model to be deleted - * @param ignoreIfNotPresent ignore errors if the specified model is not present on the Ollama - * server + * @param ignoreIfNotPresent ignore errors if the specified model is not present on the Ollama server * @throws OllamaBaseException if the response indicates an error status */ public void deleteModel(String modelName, boolean ignoreIfNotPresent) @@ -624,8 +628,8 @@ public class OllamaAPI { /** * Unloads a model from memory. - * - *

If an empty prompt is provided and the keep_alive parameter is set to 0, a model will be + *

+ * If an empty prompt is provided and the keep_alive parameter is set to 0, a model will be * unloaded from memory. * * @param modelName the name of the model to unload @@ -722,9 +726,13 @@ public class OllamaAPI { } /** - * Generates a response from a model using the specified parameters and stream observer. If - * {@code streamObserver} is provided, streaming is enabled; otherwise, a synchronous call is - * made. + * Generates a response from a model using the specified parameters and stream observer. + * If {@code streamObserver} is provided, streaming is enabled; otherwise, a synchronous call is made. + * + * @param request the generation request + * @param streamObserver the stream observer for streaming responses, or null for synchronous + * @return the result of the generation + * @throws OllamaBaseException if the request fails */ public OllamaResult generate( OllamaGenerateRequest request, OllamaGenerateStreamObserver streamObserver) @@ -751,13 +759,10 @@ public class OllamaAPI { } } + // (No javadoc for private helper, as is standard) private OllamaResult generateWithToolsInternal( OllamaGenerateRequest request, OllamaGenerateStreamObserver streamObserver) throws OllamaBaseException { - // List tools = new ArrayList<>(); - // for (Tools.ToolSpecification spec : toolRegistry.getRegisteredSpecs()) { - // tools.add(spec.getToolPrompt()); - // } ArrayList msgs = new ArrayList<>(); OllamaChatRequest chatRequest = new OllamaChatRequest(); chatRequest.setModel(request.getModel()); @@ -786,6 +791,16 @@ public class OllamaAPI { -1); } + /** + * Generates a response from a model asynchronously, returning a streamer for results. + * + * @param model the model name + * @param prompt the prompt to send + * @param raw whether to use raw mode + * @param think whether to use "think" mode + * @return an OllamaAsyncResultStreamer for streaming results + * @throws OllamaBaseException if the request fails + */ public OllamaAsyncResultStreamer generateAsync( String model, String prompt, boolean raw, boolean think) throws OllamaBaseException { long startTime = System.currentTimeMillis(); @@ -812,10 +827,10 @@ public class OllamaAPI { } /** - * Ask a question to a model using an {@link OllamaChatRequest} and set up streaming response. + * Sends a chat request to a model using an {@link OllamaChatRequest} and sets up streaming response. * This can be constructed using an {@link OllamaChatRequestBuilder}. * - *

Hint: the OllamaChatRequestModel#getStream() property is not implemented. + *

Note: the OllamaChatRequestModel#getStream() property is not implemented. * * @param request request object to be sent to the server * @param tokenHandler callback handler to handle the last token from stream (caution: the @@ -911,8 +926,8 @@ public class OllamaAPI { /** * Registers multiple tools in the tool registry. * - * @param tools a list of {@link Tools.Tool} objects to register. Each tool contains - * its specification and function. + * @param tools a list of {@link Tools.Tool} objects to register. Each tool contains its + * specification and function. */ public void registerTools(List tools) { toolRegistry.addTools(tools); @@ -927,135 +942,101 @@ public class OllamaAPI { LOG.debug("All tools have been deregistered."); } - // - // /** - // * Registers tools based on the annotations found on the methods of the caller's class and - // its - // * providers. This method scans the caller's class for the {@link OllamaToolService} - // annotation - // * and recursively registers annotated tools from all the providers specified in the - // annotation. - // * - // * @throws OllamaBaseException if the caller's class is not annotated with {@link - // * OllamaToolService} or if reflection-based instantiation or invocation fails - // */ - // public void registerAnnotatedTools() throws OllamaBaseException { - // try { - // Class callerClass = null; - // try { - // callerClass = - // - // Class.forName(Thread.currentThread().getStackTrace()[2].getClassName()); - // } catch (ClassNotFoundException e) { - // throw new OllamaBaseException(e.getMessage(), e); - // } - // - // OllamaToolService ollamaToolServiceAnnotation = - // callerClass.getDeclaredAnnotation(OllamaToolService.class); - // if (ollamaToolServiceAnnotation == null) { - // throw new IllegalStateException( - // callerClass + " is not annotated as " + OllamaToolService.class); - // } - // - // Class[] providers = ollamaToolServiceAnnotation.providers(); - // for (Class provider : providers) { - // registerAnnotatedTools(provider.getDeclaredConstructor().newInstance()); - // } - // } catch (InstantiationException - // | NoSuchMethodException - // | IllegalAccessException - // | InvocationTargetException e) { - // throw new OllamaBaseException(e.getMessage()); - // } - // } - // - // /** - // * Registers tools based on the annotations found on the methods of the provided object. - // This - // * method scans the methods of the given object and registers tools using the {@link - // ToolSpec} - // * annotation and associated {@link ToolProperty} annotations. It constructs tool - // specifications - // * and stores them in a tool registry. - // * - // * @param object the object whose methods are to be inspected for annotated tools - // * @throws RuntimeException if any reflection-based instantiation or invocation fails - // */ - // public void registerAnnotatedTools(Object object) { - // Class objectClass = object.getClass(); - // Method[] methods = objectClass.getMethods(); - // for (Method m : methods) { - // ToolSpec toolSpec = m.getDeclaredAnnotation(ToolSpec.class); - // if (toolSpec == null) { - // continue; - // } - // String operationName = !toolSpec.name().isBlank() ? toolSpec.name() : m.getName(); - // String operationDesc = !toolSpec.desc().isBlank() ? toolSpec.desc() : - // operationName; - // - // final Tools.PropsBuilder propsBuilder = new Tools.PropsBuilder(); - // LinkedHashMap methodParams = new LinkedHashMap<>(); - // for (Parameter parameter : m.getParameters()) { - // final ToolProperty toolPropertyAnn = - // parameter.getDeclaredAnnotation(ToolProperty.class); - // String propType = parameter.getType().getTypeName(); - // if (toolPropertyAnn == null) { - // methodParams.put(parameter.getName(), null); - // continue; - // } - // String propName = - // !toolPropertyAnn.name().isBlank() - // ? toolPropertyAnn.name() - // : parameter.getName(); - // methodParams.put(propName, propType); - // propsBuilder.withProperty( - // propName, - // Tools.PromptFuncDefinition.Property.builder() - // .type(propType) - // .description(toolPropertyAnn.desc()) - // .required(toolPropertyAnn.required()) - // .build()); - // } - // final Map params = - // propsBuilder.build(); - // List reqProps = - // params.entrySet().stream() - // .filter(e -> e.getValue().isRequired()) - // .map(Map.Entry::getKey) - // .collect(Collectors.toList()); - // - // Tools.ToolSpecification toolSpecification = - // Tools.ToolSpecification.builder() - // .functionName(operationName) - // .functionDescription(operationDesc) - // .toolPrompt( - // Tools.PromptFuncDefinition.builder() - // .type("function") - // .function( - // Tools.PromptFuncDefinition.PromptFuncSpec - // .builder() - // .name(operationName) - // .description(operationDesc) - // .parameters( - // Tools.PromptFuncDefinition - // - // .Parameters.builder() - // .type("object") - // - // .properties(params) - // - // .required(reqProps) - // .build()) - // .build()) - // .build()) - // .build(); - // - // ReflectionalToolFunction reflectionalToolFunction = - // new ReflectionalToolFunction(object, m, methodParams); - // toolSpecification.setToolFunction(reflectionalToolFunction); - // toolRegistry.addTool(toolSpecification.getFunctionName(), toolSpecification); - // } - // } + /** + * Registers tools based on the annotations found on the methods of the caller's class and its + * providers. This method scans the caller's class for the {@link OllamaToolService} annotation + * and recursively registers annotated tools from all the providers specified in the annotation. + * + * @throws OllamaBaseException if the caller's class is not annotated with {@link + * OllamaToolService} or if reflection-based instantiation or invocation fails + */ + public void registerAnnotatedTools() throws OllamaBaseException { + try { + Class callerClass = null; + try { + callerClass = + Class.forName(Thread.currentThread().getStackTrace()[2].getClassName()); + } catch (ClassNotFoundException e) { + throw new OllamaBaseException(e.getMessage(), e); + } + + OllamaToolService ollamaToolServiceAnnotation = + callerClass.getDeclaredAnnotation(OllamaToolService.class); + if (ollamaToolServiceAnnotation == null) { + throw new IllegalStateException( + callerClass + " is not annotated as " + OllamaToolService.class); + } + + Class[] providers = ollamaToolServiceAnnotation.providers(); + for (Class provider : providers) { + registerAnnotatedTools(provider.getDeclaredConstructor().newInstance()); + } + } catch (InstantiationException + | NoSuchMethodException + | IllegalAccessException + | InvocationTargetException e) { + throw new OllamaBaseException(e.getMessage()); + } + } + + /** + * Registers tools based on the annotations found on the methods of the provided object. + * This method scans the methods of the given object and registers tools using the {@link ToolSpec} + * annotation and associated {@link ToolProperty} annotations. It constructs tool specifications + * and stores them in a tool registry. + * + * @param object the object whose methods are to be inspected for annotated tools + * @throws RuntimeException if any reflection-based instantiation or invocation fails + */ + public void registerAnnotatedTools(Object object) { + Class objectClass = object.getClass(); + Method[] methods = objectClass.getMethods(); + for (Method m : methods) { + ToolSpec toolSpec = m.getDeclaredAnnotation(ToolSpec.class); + if (toolSpec == null) { + continue; + } + String operationName = !toolSpec.name().isBlank() ? toolSpec.name() : m.getName(); + String operationDesc = !toolSpec.desc().isBlank() ? toolSpec.desc() : operationName; + + final Map params = new HashMap() {}; + LinkedHashMap methodParams = new LinkedHashMap<>(); + for (Parameter parameter : m.getParameters()) { + final ToolProperty toolPropertyAnn = + parameter.getDeclaredAnnotation(ToolProperty.class); + String propType = parameter.getType().getTypeName(); + if (toolPropertyAnn == null) { + methodParams.put(parameter.getName(), null); + continue; + } + String propName = + !toolPropertyAnn.name().isBlank() + ? toolPropertyAnn.name() + : parameter.getName(); + methodParams.put(propName, propType); + params.put( + propName, + Tools.Property.builder() + .type(propType) + .description(toolPropertyAnn.desc()) + .required(toolPropertyAnn.required()) + .build()); + } + Tools.ToolSpec toolSpecification = + Tools.ToolSpec.builder() + .name(operationName) + .description(operationDesc) + .parameters(Tools.Parameters.of(params)) + .build(); + ReflectionalToolFunction reflectionalToolFunction = + new ReflectionalToolFunction(object, m, methodParams); + toolRegistry.addTool( + Tools.Tool.builder() + .toolFunction(reflectionalToolFunction) + .toolSpec(toolSpecification) + .build()); + } + } /** * Adds a custom role. @@ -1111,19 +1092,15 @@ public class OllamaAPI { } /** - * Generates a request for the Ollama API and returns the result. This method synchronously - * calls the Ollama API. If a stream handler is provided, the request will be streamed; - * otherwise, a regular synchronous request will be made. + * Generates a request for the Ollama API and returns the result. + * This method synchronously calls the Ollama API. If a stream handler is provided, + * the request will be streamed; otherwise, a regular synchronous request will be made. * - * @param ollamaRequestModel the request model containing necessary parameters for the Ollama - * API request + * @param ollamaRequestModel the request model containing necessary parameters for the Ollama API request * @param thinkingStreamHandler the stream handler for "thinking" tokens, or null if not used - * @param responseStreamHandler the stream handler to process streaming responses, or null for - * non-streaming requests + * @param responseStreamHandler the stream handler to process streaming responses, or null for non-streaming requests * @return the result of the Ollama API request * @throws OllamaBaseException if the request fails due to an issue with the Ollama API - * @throws IOException if an I/O error occurs during the request process - * @throws InterruptedException if the thread is interrupted during the request */ private OllamaResult generateSyncForOllamaRequestModel( OllamaGenerateRequest ollamaRequestModel, @@ -1192,157 +1169,4 @@ public class OllamaAPI { private boolean isAuthSet() { return auth != null; } - - // /** - // * Invokes a registered tool function by name and arguments. - // * - // * @param toolFunctionCallSpec the tool function call specification - // * @return the result of the tool function - // * @throws ToolInvocationException if the tool is not found or invocation fails - // */ - // private Object invokeTool(ToolFunctionCallSpec toolFunctionCallSpec) - // throws ToolInvocationException { - // try { - // String methodName = toolFunctionCallSpec.getName(); - // Map arguments = toolFunctionCallSpec.getArguments(); - // ToolFunction function = toolRegistry.getToolFunction(methodName); - // LOG.debug("Invoking function {} with arguments {}", methodName, arguments); - // if (function == null) { - // throw new ToolNotFoundException( - // "No such tool: " - // + methodName - // + ". Please register the tool before invoking it."); - // } - // return function.apply(arguments); - // } catch (Exception e) { - // throw new ToolInvocationException( - // "Failed to invoke tool: " + toolFunctionCallSpec.getName(), e); - // } - // } - - // /** - // * Initialize metrics collection if enabled. - // */ - // private void initializeMetrics() { - // if (metricsEnabled) { - // OllamaMetricsService.initialize(); - // LOG.info("Prometheus metrics collection enabled for Ollama4j client"); - // } - // } - // - // /** - // * Record metrics for an API request. - // * - // * @param endpoint the API endpoint - // * @param method the HTTP method - // * @param durationSeconds the request duration - // * @param success whether the request was successful - // * @param errorType the error type if the request failed - // */ - // private void recordMetrics( - // String endpoint, - // String method, - // double durationSeconds, - // boolean success, - // String errorType) { - // if (!metricsEnabled) { - // return; - // } - // - // if (success) { - // OllamaMetricsService.recordRequest(endpoint, method, durationSeconds); - // } else { - // OllamaMetricsService.recordRequestError(endpoint, method, durationSeconds, - // errorType); - // } - // } - - // /** - // * Record metrics for model usage. - // * - // * @param modelName the model name - // * @param operation the operation performed - // * @param durationSeconds the operation duration - // */ - // private void recordModelMetrics(String modelName, String operation, double - // durationSeconds) { - // if (!metricsEnabled) { - // return; - // } - // - // OllamaMetricsService.recordModelUsage(modelName, operation, durationSeconds); - // } - - // /** - // * Record token generation metrics. - // * - // * @param modelName the model name - // * @param tokenCount the number of tokens generated - // */ - // private void recordTokenMetrics(String modelName, int tokenCount) { - // if (!metricsEnabled) { - // return; - // } - // - // OllamaMetricsService.recordTokensGenerated(modelName, tokenCount); - // } - - // /** - // * Execute a method with metrics collection. - // * - // * @param endpoint the API endpoint - // * @param method the HTTP method - // * @param operation the operation name for model metrics - // * @param modelName the model name (can be null) - // * @param runnable the operation to execute - // * @return the result of the operation - // * @throws Exception if the operation fails - // */ - // private T executeWithMetrics( - // String endpoint, - // String method, - // String operation, - // String modelName, - // MetricsOperation runnable) - // throws Exception { - // long startTime = System.nanoTime(); - // boolean success = false; - // String errorType = null; - // - // try { - // OllamaMetricsService.incrementActiveConnections(); - // T result = runnable.execute(); - // success = true; - // return result; - // } catch (OllamaBaseException e) { - // errorType = "ollama_error"; - // throw e; - // } catch (IOException e) { - // errorType = "io_error"; - // throw e; - // } catch (InterruptedException e) { - // errorType = "interrupted"; - // throw e; - // } catch (Exception e) { - // errorType = "unknown_error"; - // throw e; - // } finally { - // OllamaMetricsService.decrementActiveConnections(); - // double durationSeconds = (System.nanoTime() - startTime) / 1_000_000_000.0; - // - // recordMetrics(endpoint, method, durationSeconds, success, errorType); - // - // if (modelName != null) { - // recordModelMetrics(modelName, operation, durationSeconds); - // } - // } - // } - - // /** - // * Functional interface for operations that need metrics collection. - // */ - // @FunctionalInterface - // private interface MetricsOperation { - // T execute() throws Exception; - // } } diff --git a/src/main/java/io/github/ollama4j/models/chat/OllamaChatMessage.java b/src/main/java/io/github/ollama4j/models/chat/OllamaChatMessage.java index f969599..ef1b3da 100644 --- a/src/main/java/io/github/ollama4j/models/chat/OllamaChatMessage.java +++ b/src/main/java/io/github/ollama4j/models/chat/OllamaChatMessage.java @@ -25,6 +25,7 @@ import lombok.*; * href="https://github.com/ollama/ollama/blob/main/docs/api.md#generate-a-chat-completion">Generate * chat completion */ +@SuppressWarnings("NullableProblems") @Data @AllArgsConstructor @RequiredArgsConstructor diff --git a/src/main/java/io/github/ollama4j/models/chat/OllamaChatMessageRole.java b/src/main/java/io/github/ollama4j/models/chat/OllamaChatMessageRole.java index 676d6c0..617fb51 100644 --- a/src/main/java/io/github/ollama4j/models/chat/OllamaChatMessageRole.java +++ b/src/main/java/io/github/ollama4j/models/chat/OllamaChatMessageRole.java @@ -34,8 +34,6 @@ public class OllamaChatMessageRole { } public static OllamaChatMessageRole newCustomRole(String roleName) { - // OllamaChatMessageRole customRole = new OllamaChatMessageRole(roleName); - // roles.add(customRole); return new OllamaChatMessageRole(roleName); } diff --git a/src/main/java/io/github/ollama4j/models/chat/OllamaChatRequestBuilder.java b/src/main/java/io/github/ollama4j/models/chat/OllamaChatRequestBuilder.java index 297723e..f72759f 100644 --- a/src/main/java/io/github/ollama4j/models/chat/OllamaChatRequestBuilder.java +++ b/src/main/java/io/github/ollama4j/models/chat/OllamaChatRequestBuilder.java @@ -36,13 +36,6 @@ public class OllamaChatRequestBuilder { request.setMessages(new ArrayList<>()); } - // private OllamaChatRequestBuilder(String model, List messages) { - // request = new OllamaChatRequest(model, false, messages); - // } - // public static OllamaChatRequestBuilder builder(String model) { - // return new OllamaChatRequestBuilder(model, new ArrayList<>()); - // } - public static OllamaChatRequestBuilder builder() { return new OllamaChatRequestBuilder(); } diff --git a/src/main/java/io/github/ollama4j/models/chat/OllamaChatResult.java b/src/main/java/io/github/ollama4j/models/chat/OllamaChatResult.java index e77f4fe..db0ddf2 100644 --- a/src/main/java/io/github/ollama4j/models/chat/OllamaChatResult.java +++ b/src/main/java/io/github/ollama4j/models/chat/OllamaChatResult.java @@ -44,19 +44,4 @@ public class OllamaChatResult { throw new RuntimeException(e); } } - - @Deprecated - public String getResponse() { - return responseModel != null ? responseModel.getMessage().getResponse() : ""; - } - - @Deprecated - public int getHttpStatusCode() { - return 200; - } - - @Deprecated - public long getResponseTime() { - return responseModel != null ? responseModel.getTotalDuration() : 0L; - } } diff --git a/src/main/java/io/github/ollama4j/models/chat/OllamaChatStreamObserver.java b/src/main/java/io/github/ollama4j/models/chat/OllamaChatStreamObserver.java index b2bf91b..776b006 100644 --- a/src/main/java/io/github/ollama4j/models/chat/OllamaChatStreamObserver.java +++ b/src/main/java/io/github/ollama4j/models/chat/OllamaChatStreamObserver.java @@ -33,12 +33,8 @@ public class OllamaChatStreamObserver implements OllamaChatTokenHandler { boolean hasResponse = response != null && !response.isEmpty(); if (!hasResponse && hasThinking && thinkingStreamHandler != null) { - // use only new tokens received, instead of appending the tokens to the previous - // ones and sending the full string again thinkingStreamHandler.accept(thinking); } else if (hasResponse) { - // use only new tokens received, instead of appending the tokens to the previous - // ones and sending the full string again responseStreamHandler.accept(response); } } diff --git a/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbedRequestBuilder.java b/src/main/java/io/github/ollama4j/models/embed/OllamaEmbedRequestBuilder.java similarity index 96% rename from src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbedRequestBuilder.java rename to src/main/java/io/github/ollama4j/models/embed/OllamaEmbedRequestBuilder.java index bee9f45..910891c 100644 --- a/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbedRequestBuilder.java +++ b/src/main/java/io/github/ollama4j/models/embed/OllamaEmbedRequestBuilder.java @@ -6,7 +6,7 @@ * you may not use this file except in compliance with the License. * */ -package io.github.ollama4j.models.embeddings; +package io.github.ollama4j.models.embed; import io.github.ollama4j.utils.Options; import java.util.List; diff --git a/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbedRequestModel.java b/src/main/java/io/github/ollama4j/models/embed/OllamaEmbedRequestModel.java similarity index 93% rename from src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbedRequestModel.java rename to src/main/java/io/github/ollama4j/models/embed/OllamaEmbedRequestModel.java index 82f70e0..1bf815a 100644 --- a/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbedRequestModel.java +++ b/src/main/java/io/github/ollama4j/models/embed/OllamaEmbedRequestModel.java @@ -6,7 +6,7 @@ * you may not use this file except in compliance with the License. * */ -package io.github.ollama4j.models.embeddings; +package io.github.ollama4j.models.embed; import static io.github.ollama4j.utils.Utils.getObjectMapper; @@ -16,6 +16,7 @@ import java.util.List; import java.util.Map; import lombok.*; +@SuppressWarnings("NullableProblems") @Data @RequiredArgsConstructor @NoArgsConstructor diff --git a/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbedResponseModel.java b/src/main/java/io/github/ollama4j/models/embed/OllamaEmbedResponseModel.java similarity index 94% rename from src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbedResponseModel.java rename to src/main/java/io/github/ollama4j/models/embed/OllamaEmbedResponseModel.java index a97354b..742af9f 100644 --- a/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbedResponseModel.java +++ b/src/main/java/io/github/ollama4j/models/embed/OllamaEmbedResponseModel.java @@ -6,7 +6,7 @@ * you may not use this file except in compliance with the License. * */ -package io.github.ollama4j.models.embeddings; +package io.github.ollama4j.models.embed; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; diff --git a/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateStreamObserver.java b/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateStreamObserver.java index d3371ea..0e908dc 100644 --- a/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateStreamObserver.java +++ b/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateStreamObserver.java @@ -39,12 +39,8 @@ public class OllamaGenerateStreamObserver { boolean hasThinking = thinking != null && !thinking.isEmpty(); if (!hasResponse && hasThinking && thinkingStreamHandler != null) { - // use only new tokens received, instead of appending the tokens to the previous - // ones and sending the full string again thinkingStreamHandler.accept(thinking); } else if (hasResponse && responseStreamHandler != null) { - // use only new tokens received, instead of appending the tokens to the previous - // ones and sending the full string again responseStreamHandler.accept(response); } } diff --git a/src/main/java/io/github/ollama4j/models/ps/ModelsProcessResponse.java b/src/main/java/io/github/ollama4j/models/ps/ModelsProcessResponse.java index 858dd4e..96cb971 100644 --- a/src/main/java/io/github/ollama4j/models/ps/ModelsProcessResponse.java +++ b/src/main/java/io/github/ollama4j/models/ps/ModelsProcessResponse.java @@ -41,7 +41,7 @@ public class ModelsProcessResponse { private ModelDetails details; @JsonProperty("expires_at") - private String expiresAt; // Consider using LocalDateTime if you need to process date/time + private String expiresAt; @JsonProperty("size_vram") private long sizeVram; diff --git a/src/main/java/io/github/ollama4j/models/request/CustomModelRequest.java b/src/main/java/io/github/ollama4j/models/request/CustomModelRequest.java index 8025a12..7cd7417 100644 --- a/src/main/java/io/github/ollama4j/models/request/CustomModelRequest.java +++ b/src/main/java/io/github/ollama4j/models/request/CustomModelRequest.java @@ -26,7 +26,7 @@ public class CustomModelRequest { private Map files; private Map adapters; private String template; - private Object license; // Using Object to handle both String and List + private Object license; private String system; private Map parameters; private List messages; diff --git a/src/main/java/io/github/ollama4j/models/request/OllamaChatEndpointCaller.java b/src/main/java/io/github/ollama4j/models/request/OllamaChatEndpointCaller.java index 5fb4ce9..952e094 100644 --- a/src/main/java/io/github/ollama4j/models/request/OllamaChatEndpointCaller.java +++ b/src/main/java/io/github/ollama4j/models/request/OllamaChatEndpointCaller.java @@ -59,10 +59,10 @@ public class OllamaChatEndpointCaller extends OllamaEndpointCaller { try { OllamaChatResponseModel ollamaResponseModel = Utils.getObjectMapper().readValue(line, OllamaChatResponseModel.class); - // it seems that under heavy load ollama responds with an empty chat message part in the - // streamed response - // thus, we null check the message and hope that the next streamed response has some - // message content again + // It seems that under heavy load Ollama responds with an empty chat message part in the + // streamed response. + // Thus, we null check the message and hope that the next streamed response has some + // message content again. OllamaChatMessage message = ollamaResponseModel.getMessage(); if (message != null) { if (message.getThinking() != null) { diff --git a/src/main/java/io/github/ollama4j/models/request/OllamaEndpointCaller.java b/src/main/java/io/github/ollama4j/models/request/OllamaEndpointCaller.java index 01ee916..85c5132 100644 --- a/src/main/java/io/github/ollama4j/models/request/OllamaEndpointCaller.java +++ b/src/main/java/io/github/ollama4j/models/request/OllamaEndpointCaller.java @@ -24,7 +24,7 @@ public abstract class OllamaEndpointCaller { private final Auth auth; private final long requestTimeoutSeconds; - public OllamaEndpointCaller(String host, Auth auth, long requestTimeoutSeconds) { + protected OllamaEndpointCaller(String host, Auth auth, long requestTimeoutSeconds) { this.host = host; this.auth = auth; this.requestTimeoutSeconds = requestTimeoutSeconds; diff --git a/src/main/java/io/github/ollama4j/models/request/OllamaGenerateEndpointCaller.java b/src/main/java/io/github/ollama4j/models/request/OllamaGenerateEndpointCaller.java index a4b5ae3..253a20e 100644 --- a/src/main/java/io/github/ollama4j/models/request/OllamaGenerateEndpointCaller.java +++ b/src/main/java/io/github/ollama4j/models/request/OllamaGenerateEndpointCaller.java @@ -86,7 +86,6 @@ public class OllamaGenerateEndpointCaller extends OllamaEndpointCaller { @SuppressWarnings("DuplicatedCode") public OllamaResult callSync(OllamaRequestBody body) throws OllamaBaseException, IOException, InterruptedException { - // Create Request long startTime = System.currentTimeMillis(); HttpClient httpClient = HttpClient.newHttpClient(); URI uri = URI.create(getHost() + endpoint); diff --git a/src/main/java/io/github/ollama4j/models/response/OllamaAsyncResultStreamer.java b/src/main/java/io/github/ollama4j/models/response/OllamaAsyncResultStreamer.java index 516e328..07df702 100644 --- a/src/main/java/io/github/ollama4j/models/response/OllamaAsyncResultStreamer.java +++ b/src/main/java/io/github/ollama4j/models/response/OllamaAsyncResultStreamer.java @@ -136,14 +136,12 @@ public class OllamaAsyncResultStreamer extends Thread { try { reader.close(); } catch (IOException e) { - // Optionally log or handle } } if (responseBodyStream != null) { try { responseBodyStream.close(); } catch (IOException e) { - // Optionally log or handle } } } diff --git a/src/main/java/io/github/ollama4j/tools/Tools.java b/src/main/java/io/github/ollama4j/tools/Tools.java index c2f5b0a..a82a717 100644 --- a/src/main/java/io/github/ollama4j/tools/Tools.java +++ b/src/main/java/io/github/ollama4j/tools/Tools.java @@ -21,6 +21,8 @@ import lombok.Data; import lombok.NoArgsConstructor; public class Tools { + private Tools() {} + @Data @Builder @NoArgsConstructor diff --git a/src/main/java/io/github/ollama4j/tools/annotations/OllamaToolService.java b/src/main/java/io/github/ollama4j/tools/annotations/OllamaToolService.java index 726e31f..d044fa5 100644 --- a/src/main/java/io/github/ollama4j/tools/annotations/OllamaToolService.java +++ b/src/main/java/io/github/ollama4j/tools/annotations/OllamaToolService.java @@ -15,16 +15,23 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Annotates a class that calls {@link io.github.ollama4j.OllamaAPI} such that the Method - * {@link OllamaAPI#registerAnnotatedTools()} can be used to auto-register all provided classes (resp. all - * contained Methods of the provider classes annotated with {@link ToolSpec}). + * Annotation to mark a class as an Ollama tool service. + *

+ * When a class is annotated with {@code @OllamaToolService}, the method + * {@link OllamaAPI#registerAnnotatedTools()} can be used to automatically register all tool provider + * classes specified in the {@link #providers()} array. All methods in those provider classes that are + * annotated with {@link ToolSpec} will be registered as tools. + *

*/ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface OllamaToolService { /** - * @return Classes with no-arg constructor that will be used for tool-registration. + * Specifies the provider classes whose methods annotated with {@link ToolSpec} should be registered as tools. + * Each provider class must have a public no-argument constructor. + * + * @return an array of provider classes to be used for tool registration */ Class[] providers(); } diff --git a/src/main/java/io/github/ollama4j/tools/annotations/ToolSpec.java b/src/main/java/io/github/ollama4j/tools/annotations/ToolSpec.java index 33bf8dc..04a3efb 100644 --- a/src/main/java/io/github/ollama4j/tools/annotations/ToolSpec.java +++ b/src/main/java/io/github/ollama4j/tools/annotations/ToolSpec.java @@ -15,21 +15,30 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Annotates Methods of classes that should be registered as tools by {@link OllamaAPI#registerAnnotatedTools()} - * automatically. + * Annotation to mark a method as a tool that can be registered automatically by + * {@link OllamaAPI#registerAnnotatedTools()}. + *

+ * Methods annotated with {@code @ToolSpec} will be discovered and registered as tools + * when the containing class is specified as a provider in {@link OllamaToolService}. + *

*/ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface ToolSpec { /** - * @return tool-name that the method should be used as. Defaults to the methods name. + * Specifies the name of the tool as exposed to the LLM. + * If left empty, the method's name will be used as the tool name. + * + * @return the tool name */ String name() default ""; /** - * @return a detailed description of the method that can be interpreted by the llm, whether it should call the tool - * or not. + * Provides a detailed description of the tool's functionality. + * This description is used by the LLM to determine when to call the tool. + * + * @return the tool description */ String desc(); } diff --git a/src/main/java/io/github/ollama4j/utils/Options.java b/src/main/java/io/github/ollama4j/utils/Options.java index 9b5333d..36b5264 100644 --- a/src/main/java/io/github/ollama4j/utils/Options.java +++ b/src/main/java/io/github/ollama4j/utils/Options.java @@ -9,12 +9,14 @@ package io.github.ollama4j.utils; import java.util.Map; +import lombok.Builder; import lombok.Data; /** * Class for options for Ollama model. */ @Data +@Builder public class Options { private final Map optionsMap; diff --git a/src/test/java/io/github/ollama4j/unittests/TestMockedAPIs.java b/src/test/java/io/github/ollama4j/unittests/TestMockedAPIs.java index 01d0741..176d662 100644 --- a/src/test/java/io/github/ollama4j/unittests/TestMockedAPIs.java +++ b/src/test/java/io/github/ollama4j/unittests/TestMockedAPIs.java @@ -16,8 +16,8 @@ import io.github.ollama4j.OllamaAPI; import io.github.ollama4j.exceptions.OllamaBaseException; import io.github.ollama4j.exceptions.RoleNotFoundException; import io.github.ollama4j.models.chat.OllamaChatMessageRole; -import io.github.ollama4j.models.embeddings.OllamaEmbedRequestModel; -import io.github.ollama4j.models.embeddings.OllamaEmbedResponseModel; +import io.github.ollama4j.models.embed.OllamaEmbedRequestModel; +import io.github.ollama4j.models.embed.OllamaEmbedResponseModel; import io.github.ollama4j.models.generate.OllamaGenerateRequest; import io.github.ollama4j.models.generate.OllamaGenerateRequestBuilder; import io.github.ollama4j.models.generate.OllamaGenerateStreamObserver; diff --git a/src/test/java/io/github/ollama4j/unittests/jackson/TestEmbedRequestSerialization.java b/src/test/java/io/github/ollama4j/unittests/jackson/TestEmbedRequestSerialization.java index 0fa2175..7cd1808 100644 --- a/src/test/java/io/github/ollama4j/unittests/jackson/TestEmbedRequestSerialization.java +++ b/src/test/java/io/github/ollama4j/unittests/jackson/TestEmbedRequestSerialization.java @@ -10,8 +10,8 @@ package io.github.ollama4j.unittests.jackson; import static org.junit.jupiter.api.Assertions.assertEquals; -import io.github.ollama4j.models.embeddings.OllamaEmbedRequestBuilder; -import io.github.ollama4j.models.embeddings.OllamaEmbedRequestModel; +import io.github.ollama4j.models.embed.OllamaEmbedRequestBuilder; +import io.github.ollama4j.models.embed.OllamaEmbedRequestModel; import io.github.ollama4j.utils.OptionsBuilder; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test;