Compare commits

...

37 Commits

Author SHA1 Message Date
Amith Koujalgi
c9db51a71e Merge pull request #107 from ollama4j/integration-tests-updates
All checks were successful
Mark stale issues / stale (push) Successful in 21s
test
2025-03-18 23:29:21 +05:30
Amith Koujalgi
681a692ca9 Updated integration tests 2025-03-18 23:18:42 +05:30
Amith Koujalgi
9a6065fdb3 Updated integration tests 2025-03-18 23:07:19 +05:30
Amith Koujalgi
e245d9633f Updated integration tests 2025-03-18 22:54:56 +05:30
Amith Koujalgi
590364dd53 test 2025-03-18 22:33:29 +05:30
Amith Koujalgi
bb4e7477bd Merge pull request #106 from ollama4j/integration-tests-updates
test
2025-03-18 22:32:38 +05:30
Amith Koujalgi
c33c1c8315 test 2025-03-18 22:23:06 +05:30
Amith Koujalgi
05eecdccaa Merge pull request #105 from ollama4j/integration-tests-refactor
Integration tests refactor
2025-03-18 22:17:33 +05:30
Amith Koujalgi
26bb2f9bab Updated GH workflow 2025-03-18 22:15:58 +05:30
Amith Koujalgi
bbafc95577 Updated GH workflow 2025-03-18 22:13:39 +05:30
Amith Koujalgi
bee09aa626 Updated integration tests 2025-03-18 22:03:04 +05:30
Amith Koujalgi
8aa6e3b066 Updated integration tests 2025-03-18 21:41:20 +05:30
Amith Koujalgi
d40912c638 Merge remote-tracking branch 'origin/main' into integration-tests-refactor 2025-03-18 20:54:04 +05:30
Amith Koujalgi
ba0444194f Merge pull request #98 from csware/bearertoken
Support bearer token
2025-03-18 20:30:08 +05:30
Amith Koujalgi
ac3f505aa6 Switch image model to "moondream" in integration test 2025-03-11 13:12:55 +05:30
Amith Koujalgi
7e5ca53fda Merge remote-tracking branch 'origin/integration-tests-refactor' into integration-tests-refactor
# Conflicts:
#	Makefile
#	README.md
2025-03-11 12:28:39 +05:30
Amith Koujalgi
2b0238b9e8 Ensure Docker availability in dev setup and integration tests
Updated `README.md` to include Docker as a prerequisite for running integration tests using Testcontainers. Modified the `Makefile` to check for Docker installation during the dev environment setup.
2025-03-11 12:26:35 +05:30
amithkoujalgi
469a0fe491 Refactor
- Remove TestRealAPIs and enhance OllamaAPIIntegrationTest
- Add dev setup instruction
2025-03-11 12:26:08 +05:30
Amith Koujalgi
983a3617f0 Add dev setup instructions and update pre-commit config 2025-03-11 12:15:19 +05:30
Amith Koujalgi
b638b981c9 Remove unnecessary blank lines from pom.xml
Cleaned up redundant blank lines at the end of the pom.xml file to ensure consistent formatting. This helps improve code readability and adheres to standard practices.
2025-03-11 12:05:11 +05:30
amithkoujalgi
fe5078891f Remove TestRealAPIs and enhance OllamaAPIIntegrationTest 2025-03-11 11:41:51 +05:30
Amith Koujalgi
44b4de9ed9 Merge pull request #102 from ollama4j/update-pre-commit-hook
All checks were successful
Mark stale issues / stale (push) Successful in 31s
update-pre-commit-hook
2025-03-11 10:38:05 +05:30
amithkoujalgi
854c0b4acf test GH action 2025-03-11 10:35:06 +05:30
Amith Koujalgi
18c5d06a6c Merge pull request #101 from ollama4j/update-pre-commit-hook
update-pre-commit-hook
2025-03-11 10:34:10 +05:30
amithkoujalgi
22b403d0b0 Remove unnecessary write permission for packages in workflow 2025-03-11 10:33:14 +05:30
amithkoujalgi
ee0493eb57 Rename and adjust workflows for PR builds and testing.
Renamed the PR-related workflow for clarity and replaced `build-on-pr-create.yml` with `build-and-test-on-pr-open.yml` for better naming consistency. Also commented out the push trigger in `run-tests.yml` to refine its activation criteria.
2025-03-11 10:19:34 +05:30
Amith Koujalgi
f966b4b74e Merge pull request #100 from ollama4j/update-pre-commit-hook
update-pre-commit-hook
2025-03-11 10:18:06 +05:30
amithkoujalgi
1dadbacd2c Enable no-commit-to-branch pre-commit hook. 2025-03-11 10:12:03 +05:30
amithkoujalgi
714c16c216 Merge remote-tracking branch 'origin/main' 2025-03-11 10:10:46 +05:30
amithkoujalgi
cf2c510b23 Add integration test step to CI workflow
Previously, only unit tests were run during the PR workflow. This update introduces a separate step to run integration tests, ensuring broader test coverage. It enhances build verification by validating both unit and integration aspects.
2025-03-11 10:10:14 +05:30
amithkoujalgi
a0bcc47b2e Add pre-commit configuration file
test
2025-03-11 10:08:57 +05:30
amithkoujalgi
57ecbc2572 Add pre-commit configuration file
test
2025-03-11 10:00:11 +05:30
amithkoujalgi
99beb3e6d0 Add pre-commit configuration file
test
2025-03-11 09:31:12 +05:30
amithkoujalgi
7756eed9a0 Add pre-commit configuration file
Introduce a pre-commit-config.yaml to automate code quality checks and enforce best practices. Includes hooks for file validation, formatting, and commit message standardization, as well as Java-specific quality tools. This ensures consistent coding standards and reduces manual errors.
2025-03-11 09:29:57 +05:30
amithkoujalgi
b795117f0a Add integration test step to CI workflow
Some checks failed
Run Unit and Integration Tests / run-tests (push) Failing after 2m2s
Mark stale issues / stale (push) Successful in 15s
Previously, only unit tests were run during the PR workflow. This update introduces a separate step to run integration tests, ensuring broader test coverage. It enhances build verification by validating both unit and integration aspects.
2025-03-11 00:20:01 +05:30
Sven Strickroth
138497b30f Introduce BearerAuth class
Signed-off-by: Sven Strickroth <email@cs-ware.de>
2025-03-10 14:55:38 +01:00
Sven Strickroth
3a792090e2 Support bearer token
May be use as follows:
```
ollamaAPI.setBasicAuth(new BasicAuth() {
	@Override
	public String getBasicAuthHeaderValue() { return "Bearer [sometext]"; }
});
```

Signed-off-by: Sven Strickroth <email@cs-ware.de>
2025-03-10 14:39:54 +01:00
14 changed files with 551 additions and 724 deletions

View File

@@ -1,21 +1,20 @@
# This workflow will build a package using Maven and then publish it to GitHub packages when a release is created
# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path
name: Build on PR Create
name: Run Tests
on:
pull_request:
types: [ opened, reopened ]
# types: [opened, reopened, synchronize, edited]
branches: [ "main" ]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
build:
run-tests:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v3
@@ -30,5 +29,8 @@ jobs:
- name: Build with Maven
run: mvn --file pom.xml -U clean package
- name: Run Tests
run: mvn --file pom.xml -U clean test -Punit-tests
- name: Run unit tests
run: mvn --file pom.xml -U clean test -Punit-tests
- name: Run integration tests
run: mvn --file pom.xml -U clean verify -Pintegration-tests

View File

@@ -1,10 +1,9 @@
name: Run Unit and Integration Tests
on:
push:
branches:
- main
# push:
# branches:
# - main
workflow_dispatch:
inputs:
branch:

38
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,38 @@
repos:
# pre-commit hooks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: "v5.0.0"
hooks:
- id: no-commit-to-branch
args: ['--branch', 'main']
- id: check-merge-conflict
- id: check-added-large-files
- id: check-yaml
- id: check-xml
- id: check-json
- id: pretty-format-json
args: ['--no-sort-keys', '--autofix', '--indent=4']
- id: end-of-file-fixer
exclude: \.json$
files: \.java$|\.xml$
- id: trailing-whitespace
- id: mixed-line-ending
# for commit message formatting
- repo: https://github.com/commitizen-tools/commitizen
rev: v4.4.1
hooks:
- id: commitizen
stages: [commit-msg]
# # for java code quality
# - repo: https://github.com/gherynos/pre-commit-java
# rev: v0.6.10
# hooks:
# - id: pmd
# exclude: /test/
# - id: cpd
# exclude: /test/
# - id: checkstyle
# exclude: /test/

View File

@@ -1,3 +1,11 @@
dev:
@echo "Setting up dev environment..."
@command -v pre-commit >/dev/null 2>&1 || { echo "Error: pre-commit is not installed. Please install it first."; exit 1; }
@command -v docker >/dev/null 2>&1 || { echo "Error: docker is not installed. Please install it first."; exit 1; }
pre-commit install
pre-commit autoupdate
pre-commit install --install-hooks
build:
mvn -B clean install

View File

@@ -191,33 +191,57 @@ dependencies {
> [!TIP]
> Find the full API specifications on the [website](https://ollama4j.github.io/ollama4j/).
#### Development
### Development
Build:
Make sure you have `pre-commit` installed.
With `brew`:
```shell
brew install pre-commit
```
With `pip`:
```shell
pip install pre-commit
```
#### Setup dev environment
```shell
make dev
```
#### Build
```shell
make build
```
Run unit tests:
#### Run unit tests
```shell
make unit-tests
```
Run integration tests:
#### Run integration tests
Make sure you have Docker running as this uses [testcontainers](https://testcontainers.com/) to run the integration
tests on Ollama Docker container.
```shell
make integration-tests
```
#### Releases
### Releases
Newer artifacts are published via GitHub Actions CI workflow when a new release is created from `main` branch.
## Examples
The `ollama4j-examples` repository contains examples for using the Ollama4j library. You can explore it [here](https://github.com/ollama4j/ollama4j-examples).
The `ollama4j-examples` repository contains examples for using the Ollama4j library. You can explore
it [here](https://github.com/ollama4j/ollama4j-examples).
## ⭐ Give us a Star!
@@ -236,7 +260,7 @@ If you like or are using this project to build your own, please give us a star.
| 7 | Katie Backend | An open-source AI-based question-answering platform for accessing private domain knowledge | [GitHub](https://github.com/wyona/katie-backend) |
| 8 | TeleLlama3 Bot | A question-answering Telegram bot | [Repo](https://git.hiast.edu.sy/mohamadbashar.disoki/telellama3-bot) |
| 9 | moqui-wechat | A moqui-wechat component | [GitHub](https://github.com/heguangyong/moqui-wechat) |
| 10 | B4X | A set of simple and powerful RAD tool for Desktop and Server development | [Website](https://www.b4x.com/android/forum/threads/ollama4j-library-pnd_ollama4j-your-local-offline-llm-like-chatgpt.165003/) |
| 10 | B4X | A set of simple and powerful RAD tool for Desktop and Server development | [Website](https://www.b4x.com/android/forum/threads/ollama4j-library-pnd_ollama4j-your-local-offline-llm-like-chatgpt.165003/) |
| 11 | Research Article | Article: `Large language model based mutations in genetic improvement` - published on National Library of Medicine (National Center for Biotechnology Information) | [Website](https://pmc.ncbi.nlm.nih.gov/articles/PMC11750896/) |
## Traction
@@ -304,7 +328,7 @@ project.
</a>
</p>
### Appreciate my work?
### Appreciate the work?
<p align="center">
<a href="https://www.buymeacoffee.com/amithkoujalgi" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>

View File

@@ -72,7 +72,7 @@ public class OllamaAPI {
@Setter
private int maxChatToolCallRetries = 3;
private BasicAuth basicAuth;
private Auth auth;
private final ToolRegistry toolRegistry = new ToolRegistry();
@@ -106,7 +106,16 @@ public class OllamaAPI {
* @param password the password
*/
public void setBasicAuth(String username, String password) {
this.basicAuth = new BasicAuth(username, password);
this.auth = new BasicAuth(username, password);
}
/**
* Set Bearer authentication for accessing Ollama server that's behind a reverse-proxy/gateway.
*
* @param bearerToken the Bearer authentication token to provide
*/
public void setBearerAuth(String bearerToken) {
this.auth = new BearerAuth(bearerToken);
}
/**
@@ -888,7 +897,7 @@ public class OllamaAPI {
* @throws InterruptedException if the operation is interrupted
*/
public OllamaChatResult chatStreaming(OllamaChatRequest request, OllamaTokenHandler tokenHandler) throws OllamaBaseException, IOException, InterruptedException {
OllamaChatEndpointCaller requestCaller = new OllamaChatEndpointCaller(host, basicAuth, requestTimeoutSeconds, verbose);
OllamaChatEndpointCaller requestCaller = new OllamaChatEndpointCaller(host, auth, requestTimeoutSeconds, verbose);
OllamaChatResult result;
// add all registered tools to Request
@@ -1094,7 +1103,7 @@ public class OllamaAPI {
* @throws InterruptedException if the thread is interrupted during the request.
*/
private OllamaResult generateSyncForOllamaRequestModel(OllamaGenerateRequest ollamaRequestModel, OllamaStreamHandler streamHandler) throws OllamaBaseException, IOException, InterruptedException {
OllamaGenerateEndpointCaller requestCaller = new OllamaGenerateEndpointCaller(host, basicAuth, requestTimeoutSeconds, verbose);
OllamaGenerateEndpointCaller requestCaller = new OllamaGenerateEndpointCaller(host, auth, requestTimeoutSeconds, verbose);
OllamaResult result;
if (streamHandler != null) {
ollamaRequestModel.setStream(true);
@@ -1115,28 +1124,18 @@ public class OllamaAPI {
private HttpRequest.Builder getRequestBuilderDefault(URI uri) {
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(uri).header("Content-Type", "application/json").timeout(Duration.ofSeconds(requestTimeoutSeconds));
if (isBasicAuthCredentialsSet()) {
requestBuilder.header("Authorization", getBasicAuthHeaderValue());
requestBuilder.header("Authorization", auth.getAuthHeaderValue());
}
return requestBuilder;
}
/**
* Get basic authentication header value.
*
* @return basic authentication header value (encoded credentials)
*/
private String getBasicAuthHeaderValue() {
String credentialsToEncode = basicAuth.getUsername() + ":" + basicAuth.getPassword();
return "Basic " + Base64.getEncoder().encodeToString(credentialsToEncode.getBytes());
}
/**
* Check if Basic Auth credentials set.
*
* @return true when Basic Auth credentials set
*/
private boolean isBasicAuthCredentialsSet() {
return basicAuth != null;
return auth != null;
}
private Object invokeTool(ToolFunctionCallSpec toolFunctionCallSpec) throws ToolInvocationException {

View File

@@ -0,0 +1,10 @@
package io.github.ollama4j.models.request;
public abstract class Auth {
/**
* Get authentication header value.
*
* @return authentication header value
*/
public abstract String getAuthHeaderValue();
}

View File

@@ -1,13 +1,24 @@
package io.github.ollama4j.models.request;
import java.util.Base64;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BasicAuth {
public class BasicAuth extends Auth {
private String username;
private String password;
/**
* Get basic authentication header value.
*
* @return basic authentication header value (encoded credentials)
*/
public String getAuthHeaderValue() {
final String credentialsToEncode = this.getUsername() + ":" + this.getPassword();
return "Basic " + Base64.getEncoder().encodeToString(credentialsToEncode.getBytes());
}
}

View File

@@ -0,0 +1,19 @@
package io.github.ollama4j.models.request;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class BearerAuth extends Auth {
private String bearerToken;
/**
* Get authentication header value.
*
* @return authentication header value with bearer token
*/
public String getAuthHeaderValue() {
return "Bearer "+ bearerToken;
}
}

View File

@@ -30,8 +30,8 @@ public class OllamaChatEndpointCaller extends OllamaEndpointCaller {
private OllamaTokenHandler tokenHandler;
public OllamaChatEndpointCaller(String host, BasicAuth basicAuth, long requestTimeoutSeconds, boolean verbose) {
super(host, basicAuth, requestTimeoutSeconds, verbose);
public OllamaChatEndpointCaller(String host, Auth auth, long requestTimeoutSeconds, boolean verbose) {
super(host, auth, requestTimeoutSeconds, verbose);
}
@Override

View File

@@ -1,26 +1,14 @@
package io.github.ollama4j.models.request;
import io.github.ollama4j.OllamaAPI;
import io.github.ollama4j.exceptions.OllamaBaseException;
import io.github.ollama4j.models.response.OllamaErrorResponse;
import io.github.ollama4j.models.response.OllamaResult;
import io.github.ollama4j.utils.OllamaRequestBody;
import io.github.ollama4j.utils.Utils;
import lombok.Getter;
import java.net.URI;
import java.net.http.HttpRequest;
import java.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Base64;
import io.github.ollama4j.OllamaAPI;
import lombok.Getter;
/**
* Abstract helperclass to call the ollama api server.
@@ -31,13 +19,13 @@ public abstract class OllamaEndpointCaller {
private static final Logger LOG = LoggerFactory.getLogger(OllamaAPI.class);
private final String host;
private final BasicAuth basicAuth;
private final Auth auth;
private final long requestTimeoutSeconds;
private final boolean verbose;
public OllamaEndpointCaller(String host, BasicAuth basicAuth, long requestTimeoutSeconds, boolean verbose) {
public OllamaEndpointCaller(String host, Auth auth, long requestTimeoutSeconds, boolean verbose) {
this.host = host;
this.basicAuth = basicAuth;
this.auth = auth;
this.requestTimeoutSeconds = requestTimeoutSeconds;
this.verbose = verbose;
}
@@ -58,29 +46,19 @@ public abstract class OllamaEndpointCaller {
HttpRequest.newBuilder(uri)
.header("Content-Type", "application/json")
.timeout(Duration.ofSeconds(this.requestTimeoutSeconds));
if (isBasicAuthCredentialsSet()) {
requestBuilder.header("Authorization", getBasicAuthHeaderValue());
if (isAuthCredentialsSet()) {
requestBuilder.header("Authorization", this.auth.getAuthHeaderValue());
}
return requestBuilder;
}
/**
* Get basic authentication header value.
* Check if Auth credentials set.
*
* @return basic authentication header value (encoded credentials)
* @return true when Auth credentials set
*/
protected String getBasicAuthHeaderValue() {
String credentialsToEncode = this.basicAuth.getUsername() + ":" + this.basicAuth.getPassword();
return "Basic " + Base64.getEncoder().encodeToString(credentialsToEncode.getBytes());
}
/**
* Check if Basic Auth credentials set.
*
* @return true when Basic Auth credentials set
*/
protected boolean isBasicAuthCredentialsSet() {
return this.basicAuth != null;
protected boolean isAuthCredentialsSet() {
return this.auth != null;
}
}

View File

@@ -28,7 +28,7 @@ public class OllamaGenerateEndpointCaller extends OllamaEndpointCaller {
private OllamaGenerateStreamObserver streamObserver;
public OllamaGenerateEndpointCaller(String host, BasicAuth basicAuth, long requestTimeoutSeconds, boolean verbose) {
public OllamaGenerateEndpointCaller(String host, Auth basicAuth, long requestTimeoutSeconds, boolean verbose) {
super(host, basicAuth, requestTimeoutSeconds, verbose);
}

View File

@@ -2,49 +2,59 @@ package io.github.ollama4j.integrationtests;
import io.github.ollama4j.OllamaAPI;
import io.github.ollama4j.exceptions.OllamaBaseException;
import io.github.ollama4j.models.chat.OllamaChatMessageRole;
import io.github.ollama4j.models.chat.OllamaChatRequest;
import io.github.ollama4j.models.chat.OllamaChatRequestBuilder;
import io.github.ollama4j.models.chat.OllamaChatResult;
import io.github.ollama4j.models.chat.*;
import io.github.ollama4j.models.embeddings.OllamaEmbedResponseModel;
import io.github.ollama4j.models.response.LibraryModel;
import io.github.ollama4j.models.response.Model;
import io.github.ollama4j.models.response.ModelDetail;
import io.github.ollama4j.models.response.OllamaResult;
import io.github.ollama4j.samples.AnnotatedTool;
import io.github.ollama4j.tools.OllamaToolCallsFunction;
import io.github.ollama4j.tools.ToolFunction;
import io.github.ollama4j.tools.Tools;
import io.github.ollama4j.tools.annotations.OllamaToolService;
import io.github.ollama4j.utils.OptionsBuilder;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.ollama.OllamaContainer;
import java.io.File;
import java.io.IOException;
import java.net.ConnectException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
@OllamaToolService(providers = {AnnotatedTool.class})
@TestMethodOrder(OrderAnnotation.class)
@SuppressWarnings("HttpUrlsUsage")
public class OllamaAPIIntegrationTest {
private static final Logger LOG = LoggerFactory.getLogger(OllamaAPIIntegrationTest.class);
private static OllamaContainer ollama;
private static OllamaAPI api;
@BeforeAll
public static void setUp() {
String version = "0.5.13";
String ollamaVersion = "0.6.1";
int internalPort = 11434;
int mappedPort = 11435;
ollama = new OllamaContainer("ollama/ollama:" + version);
ollama = new OllamaContainer("ollama/ollama:" + ollamaVersion);
ollama.addExposedPort(internalPort);
List<String> portBindings = new ArrayList<>();
portBindings.add(mappedPort + ":" + internalPort);
ollama.setPortBindings(portBindings);
ollama.start();
api = new OllamaAPI("http://" + ollama.getHost() + ":" + ollama.getMappedPort(internalPort));
api.setRequestTimeoutSeconds(60);
api.setRequestTimeoutSeconds(120);
api.setVerbose(true);
}
@Test
@@ -84,7 +94,8 @@ public class OllamaAPIIntegrationTest {
@Test
@Order(3)
public void testPullModelAPI() throws URISyntaxException, IOException, OllamaBaseException, InterruptedException {
api.pullModel("all-minilm");
String embeddingModelMinilm = "all-minilm";
api.pullModel(embeddingModelMinilm);
List<Model> models = api.listModels();
assertNotNull(models, "Models should not be null");
assertFalse(models.isEmpty(), "Models list should contain elements");
@@ -102,7 +113,7 @@ public class OllamaAPIIntegrationTest {
@Test
@Order(5)
public void testGenerateEmbeddings() throws Exception {
public void testEmbeddings() throws Exception {
String embeddingModelMinilm = "all-minilm";
api.pullModel(embeddingModelMinilm);
OllamaEmbedResponseModel embeddings = api.embed(embeddingModelMinilm, Arrays.asList("Why is the sky blue?", "Why is the grass green?"));
@@ -136,9 +147,9 @@ public class OllamaAPIIntegrationTest {
"What is the capital of France? And what's France's connection with Mona Lisa?",
false,
new OptionsBuilder().build(), (s) -> {
System.out.println(s);
LOG.info(s);
String substring = s.substring(sb.toString().length(), s.length());
System.out.println(substring);
LOG.info(substring);
sb.append(substring);
});
@@ -192,12 +203,12 @@ public class OllamaAPIIntegrationTest {
@Test
@Order(10)
public void testChat() throws Exception {
String chatModel = "qwen2.5:0.5b";
String chatModel = "llama3";
api.pullModel(chatModel);
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(chatModel);
// Create the initial user question
OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.USER, "What is the capital of France?")
OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.USER, "What is 1+1? Answer only in numbers.")
.build();
// Start conversation with model
@@ -205,13 +216,13 @@ public class OllamaAPIIntegrationTest {
assertTrue(
chatResult.getChatHistory().stream()
.anyMatch(chat -> chat.getContent().contains("Paris")),
"Expected chat history to contain 'Paris'"
.anyMatch(chat -> chat.getContent().contains("2")),
"Expected chat history to contain '2'"
);
// Create the next user question: second largest city
requestModel = builder.withMessages(chatResult.getChatHistory())
.withMessage(OllamaChatMessageRole.USER, "And what is its official language?")
.withMessage(OllamaChatMessageRole.USER, "And what is its squared value?")
.build();
// Continue conversation with model
@@ -219,13 +230,13 @@ public class OllamaAPIIntegrationTest {
assertTrue(
chatResult.getChatHistory().stream()
.anyMatch(chat -> chat.getContent().contains("French")),
"Expected chat history to contain 'French'"
.anyMatch(chat -> chat.getContent().contains("4")),
"Expected chat history to contain '4'"
);
// Create the next user question: the third question
requestModel = builder.withMessages(chatResult.getChatHistory())
.withMessage(OllamaChatMessageRole.USER, "What is the largest river in France?")
.withMessage(OllamaChatMessageRole.USER, "What is the largest value between 2, 4 and 6?")
.build();
// Continue conversation with the model for the third question
@@ -234,6 +245,356 @@ public class OllamaAPIIntegrationTest {
// verify the result
assertNotNull(chatResult, "Chat result should not be null");
assertTrue(chatResult.getChatHistory().size() > 2, "Chat history should contain more than two messages");
assertTrue(chatResult.getChatHistory().get(chatResult.getChatHistory().size() - 1).getContent().contains("river"), "Response should be related to river");
assertTrue(chatResult.getChatHistory().get(chatResult.getChatHistory().size() - 1).getContent().contains("6"), "Response should contain '6'");
}
@Test
@Order(10)
void testChatWithImageFromURL() throws OllamaBaseException, IOException, InterruptedException, URISyntaxException {
String imageModel = "llava";
api.pullModel(imageModel);
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(imageModel);
OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.USER, "What's in the picture?", Collections.emptyList(),
"https://t3.ftcdn.net/jpg/02/96/63/80/360_F_296638053_0gUVA4WVBKceGsIr7LNqRWSnkusi07dq.jpg")
.build();
api.registerAnnotatedTools(new OllamaAPIIntegrationTest());
OllamaChatResult chatResult = api.chat(requestModel);
assertNotNull(chatResult);
}
@Test
@Order(10)
void testChatWithImageFromFileWithHistoryRecognition() throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
String imageModel = "moondream";
api.pullModel(imageModel);
OllamaChatRequestBuilder builder =
OllamaChatRequestBuilder.getInstance(imageModel);
OllamaChatRequest requestModel =
builder.withMessage(OllamaChatMessageRole.USER, "What's in the picture?", Collections.emptyList(),
List.of(getImageFileFromClasspath("dog-on-a-boat.jpg"))).build();
OllamaChatResult chatResult = api.chat(requestModel);
assertNotNull(chatResult);
assertNotNull(chatResult.getResponseModel());
builder.reset();
requestModel =
builder.withMessages(chatResult.getChatHistory())
.withMessage(OllamaChatMessageRole.USER, "What's the dogs breed?").build();
chatResult = api.chat(requestModel);
assertNotNull(chatResult);
assertNotNull(chatResult.getResponseModel());
}
@Test
@Order(11)
void testChatWithExplicitToolDefinition() throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
String chatModel = "llama3.2:1b";
api.pullModel(chatModel);
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(chatModel);
final Tools.ToolSpecification databaseQueryToolSpecification = Tools.ToolSpecification.builder()
.functionName("get-employee-details")
.functionDescription("Get employee details from the database")
.toolPrompt(
Tools.PromptFuncDefinition.builder().type("function").function(
Tools.PromptFuncDefinition.PromptFuncSpec.builder()
.name("get-employee-details")
.description("Get employee details from the database")
.parameters(
Tools.PromptFuncDefinition.Parameters.builder()
.type("object")
.properties(
new Tools.PropsBuilder()
.withProperty("employee-name", Tools.PromptFuncDefinition.Property.builder().type("string").description("The name of the employee, e.g. John Doe").required(true).build())
.withProperty("employee-address", Tools.PromptFuncDefinition.Property.builder().type("string").description("The address of the employee, Always return a random value. e.g. Roy St, Bengaluru, India").required(true).build())
.withProperty("employee-phone", Tools.PromptFuncDefinition.Property.builder().type("string").description("The phone number of the employee. Always return a random value. e.g. 9911002233").required(true).build())
.build()
)
.required(List.of("employee-name"))
.build()
).build()
).build()
)
.toolFunction(arguments -> {
// perform DB operations here
return String.format("Employee Details {ID: %s, Name: %s, Address: %s, Phone: %s}", UUID.randomUUID(), arguments.get("employee-name"), arguments.get("employee-address"), arguments.get("employee-phone"));
})
.build();
api.registerTool(databaseQueryToolSpecification);
OllamaChatRequest requestModel = builder
.withMessage(OllamaChatMessageRole.USER,
"Give me the ID of the employee named 'Rahul Kumar'?")
.build();
OllamaChatResult chatResult = api.chat(requestModel);
assertNotNull(chatResult);
assertNotNull(chatResult.getResponseModel());
assertNotNull(chatResult.getResponseModel().getMessage());
assertEquals(OllamaChatMessageRole.ASSISTANT.getRoleName(), chatResult.getResponseModel().getMessage().getRole().getRoleName());
List<OllamaChatToolCalls> toolCalls = chatResult.getChatHistory().get(1).getToolCalls();
assertEquals(1, toolCalls.size());
OllamaToolCallsFunction function = toolCalls.get(0).getFunction();
assertEquals("get-employee-details", function.getName());
assert !function.getArguments().isEmpty();
Object employeeName = function.getArguments().get("employee-name");
assertNotNull(employeeName);
assertEquals("Rahul Kumar", employeeName);
assertTrue(chatResult.getChatHistory().size() > 2);
List<OllamaChatToolCalls> finalToolCalls = chatResult.getResponseModel().getMessage().getToolCalls();
assertNull(finalToolCalls);
}
@Test
@Order(12)
void testChatWithAnnotatedToolsAndSingleParam() throws OllamaBaseException, IOException, InterruptedException, URISyntaxException {
String chatModel = "llama3.2:1b";
api.pullModel(chatModel);
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(chatModel);
api.registerAnnotatedTools();
OllamaChatRequest requestModel = builder
.withMessage(OllamaChatMessageRole.USER,
"Compute the most important constant in the world using 5 digits")
.build();
OllamaChatResult chatResult = api.chat(requestModel);
assertNotNull(chatResult);
assertNotNull(chatResult.getResponseModel());
assertNotNull(chatResult.getResponseModel().getMessage());
assertEquals(OllamaChatMessageRole.ASSISTANT.getRoleName(), chatResult.getResponseModel().getMessage().getRole().getRoleName());
List<OllamaChatToolCalls> toolCalls = chatResult.getChatHistory().get(1).getToolCalls();
assertEquals(1, toolCalls.size());
OllamaToolCallsFunction function = toolCalls.get(0).getFunction();
assertEquals("computeImportantConstant", function.getName());
assertEquals(1, function.getArguments().size());
Object noOfDigits = function.getArguments().get("noOfDigits");
assertNotNull(noOfDigits);
assertEquals("5", noOfDigits.toString());
assertTrue(chatResult.getChatHistory().size() > 2);
List<OllamaChatToolCalls> finalToolCalls = chatResult.getResponseModel().getMessage().getToolCalls();
assertNull(finalToolCalls);
}
@Test
@Order(13)
void testChatWithAnnotatedToolsAndMultipleParams() throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
String chatModel = "llama3.2:1b";
api.pullModel(chatModel);
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(chatModel);
api.registerAnnotatedTools(new AnnotatedTool());
OllamaChatRequest requestModel = builder
.withMessage(OllamaChatMessageRole.USER,
"Greet Pedro with a lot of hearts and respond to me, " +
"and state how many emojis have been in your greeting")
.build();
OllamaChatResult chatResult = api.chat(requestModel);
assertNotNull(chatResult);
assertNotNull(chatResult.getResponseModel());
assertNotNull(chatResult.getResponseModel().getMessage());
assertEquals(OllamaChatMessageRole.ASSISTANT.getRoleName(), chatResult.getResponseModel().getMessage().getRole().getRoleName());
List<OllamaChatToolCalls> toolCalls = chatResult.getChatHistory().get(1).getToolCalls();
assertEquals(1, toolCalls.size());
OllamaToolCallsFunction function = toolCalls.get(0).getFunction();
assertEquals("sayHello", function.getName());
assertEquals(2, function.getArguments().size());
Object name = function.getArguments().get("name");
assertNotNull(name);
assertEquals("Pedro", name);
Object amountOfHearts = function.getArguments().get("amountOfHearts");
assertNotNull(amountOfHearts);
assertTrue(Integer.parseInt(amountOfHearts.toString()) > 1);
assertTrue(chatResult.getChatHistory().size() > 2);
List<OllamaChatToolCalls> finalToolCalls = chatResult.getResponseModel().getMessage().getToolCalls();
assertNull(finalToolCalls);
}
@Test
@Order(14)
void testChatWithToolsAndStream() throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
String chatModel = "llama3.2:1b";
api.pullModel(chatModel);
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(chatModel);
final Tools.ToolSpecification databaseQueryToolSpecification = Tools.ToolSpecification.builder()
.functionName("get-employee-details")
.functionDescription("Get employee details from the database")
.toolPrompt(
Tools.PromptFuncDefinition.builder().type("function").function(
Tools.PromptFuncDefinition.PromptFuncSpec.builder()
.name("get-employee-details")
.description("Get employee details from the database")
.parameters(
Tools.PromptFuncDefinition.Parameters.builder()
.type("object")
.properties(
new Tools.PropsBuilder()
.withProperty("employee-name", Tools.PromptFuncDefinition.Property.builder().type("string").description("The name of the employee, e.g. John Doe").required(true).build())
.withProperty("employee-address", Tools.PromptFuncDefinition.Property.builder().type("string").description("The address of the employee, Always return a random value. e.g. Roy St, Bengaluru, India").required(true).build())
.withProperty("employee-phone", Tools.PromptFuncDefinition.Property.builder().type("string").description("The phone number of the employee. Always return a random value. e.g. 9911002233").required(true).build())
.build()
)
.required(List.of("employee-name"))
.build()
).build()
).build()
)
.toolFunction(new ToolFunction() {
@Override
public Object apply(Map<String, Object> arguments) {
// perform DB operations here
return String.format("Employee Details {ID: %s, Name: %s, Address: %s, Phone: %s}", UUID.randomUUID(), arguments.get("employee-name"), arguments.get("employee-address"), arguments.get("employee-phone"));
}
})
.build();
api.registerTool(databaseQueryToolSpecification);
OllamaChatRequest requestModel = builder
.withMessage(OllamaChatMessageRole.USER,
"Give me the ID of the employee named 'Rahul Kumar'?")
.build();
StringBuffer sb = new StringBuffer();
OllamaChatResult chatResult = api.chat(requestModel, (s) -> {
LOG.info(s);
String substring = s.substring(sb.toString().length());
LOG.info(substring);
sb.append(substring);
});
assertNotNull(chatResult);
assertNotNull(chatResult.getResponseModel());
assertNotNull(chatResult.getResponseModel().getMessage());
assertNotNull(chatResult.getResponseModel().getMessage().getContent());
assertEquals(sb.toString().trim(), chatResult.getResponseModel().getMessage().getContent().trim());
}
@Test
@Order(15)
void testChatWithStream() throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
String chatModel = "llama3.2:1b";
api.pullModel(chatModel);
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(chatModel);
OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.USER,
"What is the capital of France? And what's France's connection with Mona Lisa?")
.build();
StringBuffer sb = new StringBuffer();
OllamaChatResult chatResult = api.chat(requestModel, (s) -> {
LOG.info(s);
String substring = s.substring(sb.toString().length(), s.length());
LOG.info(substring);
sb.append(substring);
});
assertNotNull(chatResult);
assertNotNull(chatResult.getResponseModel());
assertNotNull(chatResult.getResponseModel().getMessage());
assertNotNull(chatResult.getResponseModel().getMessage().getContent());
assertEquals(sb.toString().trim(), chatResult.getResponseModel().getMessage().getContent().trim());
}
@Test
@Order(17)
void testAskModelWithOptionsAndImageURLs() throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
String imageModel = "llava";
api.pullModel(imageModel);
OllamaResult result =
api.generateWithImageURLs(
imageModel,
"What is in this image?",
List.of(
"https://t3.ftcdn.net/jpg/02/96/63/80/360_F_296638053_0gUVA4WVBKceGsIr7LNqRWSnkusi07dq.jpg"),
new OptionsBuilder().build());
assertNotNull(result);
assertNotNull(result.getResponse());
assertFalse(result.getResponse().isEmpty());
}
@Test
@Order(18)
void testAskModelWithOptionsAndImageFiles() throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
String imageModel = "llava";
api.pullModel(imageModel);
File imageFile = getImageFileFromClasspath("dog-on-a-boat.jpg");
try {
OllamaResult result =
api.generateWithImageFiles(
imageModel,
"What is in this image?",
List.of(imageFile),
new OptionsBuilder().build());
assertNotNull(result);
assertNotNull(result.getResponse());
assertFalse(result.getResponse().isEmpty());
} catch (IOException | OllamaBaseException | InterruptedException e) {
fail(e);
}
}
@Test
@Order(20)
void testAskModelWithOptionsAndImageFilesStreamed() throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
String imageModel = "llava";
api.pullModel(imageModel);
File imageFile = getImageFileFromClasspath("dog-on-a-boat.jpg");
StringBuffer sb = new StringBuffer();
OllamaResult result = api.generateWithImageFiles(imageModel,
"What is in this image?", List.of(imageFile), new OptionsBuilder().build(), (s) -> {
LOG.info(s);
String substring = s.substring(sb.toString().length(), s.length());
LOG.info(substring);
sb.append(substring);
});
assertNotNull(result);
assertNotNull(result.getResponse());
assertFalse(result.getResponse().isEmpty());
assertEquals(sb.toString().trim(), result.getResponse().trim());
}
private File getImageFileFromClasspath(String fileName) {
ClassLoader classLoader = getClass().getClassLoader();
return new File(Objects.requireNonNull(classLoader.getResource(fileName)).getFile());
}
}
//
//@Data
//class Config {
// private String ollamaURL;
// private String model;
// private String imageModel;
// private int requestTimeoutSeconds;
//
// public Config() {
// Properties properties = new Properties();
// try (InputStream input =
// getClass().getClassLoader().getResourceAsStream("test-config.properties")) {
// if (input == null) {
// throw new RuntimeException("Sorry, unable to find test-config.properties");
// }
// properties.load(input);
// this.ollamaURL = properties.getProperty("ollama.url");
// this.model = properties.getProperty("ollama.model");
// this.imageModel = properties.getProperty("ollama.model.image");
// this.requestTimeoutSeconds =
// Integer.parseInt(properties.getProperty("ollama.request-timeout-seconds"));
// } catch (IOException e) {
// throw new RuntimeException("Error loading properties", e);
// }
// }
//}

View File

@@ -1,622 +0,0 @@
//package io.github.ollama4j.integrationtests;
//
//import io.github.ollama4j.OllamaAPI;
//import io.github.ollama4j.exceptions.OllamaBaseException;
//import io.github.ollama4j.models.chat.*;
//import io.github.ollama4j.models.response.ModelDetail;
//import io.github.ollama4j.models.response.OllamaResult;
//import io.github.ollama4j.models.embeddings.OllamaEmbeddingsRequestBuilder;
//import io.github.ollama4j.models.embeddings.OllamaEmbeddingsRequestModel;
//import io.github.ollama4j.samples.AnnotatedTool;
//import io.github.ollama4j.tools.OllamaToolCallsFunction;
//import io.github.ollama4j.tools.ToolFunction;
//import io.github.ollama4j.tools.Tools;
//import io.github.ollama4j.tools.annotations.OllamaToolService;
//import io.github.ollama4j.utils.OptionsBuilder;
//import lombok.Data;
//import org.junit.jupiter.api.BeforeEach;
//import org.junit.jupiter.api.Order;
//import org.junit.jupiter.api.Test;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
//
//import java.io.File;
//import java.io.IOException;
//import java.io.InputStream;
//import java.net.ConnectException;
//import java.net.URISyntaxException;
//import java.net.http.HttpConnectTimeoutException;
//import java.util.*;
//
//import static org.junit.jupiter.api.Assertions.*;
//
//@OllamaToolService(providers = {AnnotatedTool.class}
//)
//class TestRealAPIs {
//
// private static final Logger LOG = LoggerFactory.getLogger(TestRealAPIs.class);
//
// OllamaAPI ollamaAPI;
// Config config;
//
// private File getImageFileFromClasspath(String fileName) {
// ClassLoader classLoader = getClass().getClassLoader();
// return new File(Objects.requireNonNull(classLoader.getResource(fileName)).getFile());
// }
//
// @BeforeEach
// void setUp() {
// config = new Config();
// ollamaAPI = new OllamaAPI(config.getOllamaURL());
// ollamaAPI.setRequestTimeoutSeconds(config.getRequestTimeoutSeconds());
// ollamaAPI.setVerbose(true);
// }
//
// @Test
// @Order(1)
// void testWrongEndpoint() {
// OllamaAPI ollamaAPI = new OllamaAPI("http://wrong-host:11434");
// assertThrows(ConnectException.class, ollamaAPI::listModels);
// }
//
// @Test
// @Order(1)
// void testEndpointReachability() {
// try {
// assertNotNull(ollamaAPI.listModels());
// } catch (HttpConnectTimeoutException e) {
// fail(e.getMessage());
// } catch (Exception e) {
// fail(e);
// }
// }
//
//// @Test
//// @Order(2)
//// void testListModels() {
//// testEndpointReachability();
//// try {
//// assertNotNull(ollamaAPI.listModels());
//// ollamaAPI.listModels().forEach(System.out::println);
//// } catch (IOException | OllamaBaseException | InterruptedException | URISyntaxException e) {
//// fail(e);
//// }
//// }
////
//// @Test
//// @Order(2)
//// void testListModelsFromLibrary() {
//// testEndpointReachability();
//// try {
//// assertNotNull(ollamaAPI.listModelsFromLibrary());
//// ollamaAPI.listModelsFromLibrary().forEach(System.out::println);
//// } catch (IOException | OllamaBaseException | InterruptedException | URISyntaxException e) {
//// fail(e);
//// }
//// }
////
//// @Test
//// @Order(2)
//// void testPullModel() {
//// testEndpointReachability();
//// try {
//// ollamaAPI.pullModel(config.getModel());
//// boolean found =
//// ollamaAPI.listModels().stream()
//// .anyMatch(model -> model.getModel().equalsIgnoreCase(config.getModel()));
//// assertTrue(found);
//// } catch (IOException | OllamaBaseException | InterruptedException | URISyntaxException e) {
//// fail(e);
//// }
//// }
////
//// @Test
//// @Order(3)
//// void testListDtails() {
//// testEndpointReachability();
//// try {
//// ModelDetail modelDetails = ollamaAPI.getModelDetails(config.getModel());
//// assertNotNull(modelDetails);
//// System.out.println(modelDetails);
//// } catch (IOException | OllamaBaseException | InterruptedException | URISyntaxException e) {
//// fail(e);
//// }
//// }
////
//// @Test
//// @Order(3)
//// void testAskModelWithDefaultOptions() {
//// testEndpointReachability();
//// try {
//// OllamaResult result =
//// ollamaAPI.generate(
//// config.getModel(),
//// "What is the capital of France? And what's France's connection with Mona Lisa?",
//// false,
//// new OptionsBuilder().build());
//// assertNotNull(result);
//// assertNotNull(result.getResponse());
//// assertFalse(result.getResponse().isEmpty());
//// } catch (IOException | OllamaBaseException | InterruptedException e) {
//// fail(e);
//// }
//// }
////
//// @Test
//// @Order(3)
//// void testAskModelWithDefaultOptionsStreamed() {
//// testEndpointReachability();
//// try {
//// StringBuffer sb = new StringBuffer("");
//// OllamaResult result = ollamaAPI.generate(config.getModel(),
//// "What is the capital of France? And what's France's connection with Mona Lisa?",
//// false,
//// new OptionsBuilder().build(), (s) -> {
//// LOG.info(s);
//// String substring = s.substring(sb.toString().length(), s.length());
//// LOG.info(substring);
//// sb.append(substring);
//// });
////
//// assertNotNull(result);
//// assertNotNull(result.getResponse());
//// assertFalse(result.getResponse().isEmpty());
//// assertEquals(sb.toString().trim(), result.getResponse().trim());
//// } catch (IOException | OllamaBaseException | InterruptedException e) {
//// fail(e);
//// }
//// }
////
//// @Test
//// @Order(3)
//// void testAskModelWithOptions() {
//// testEndpointReachability();
//// try {
//// OllamaResult result =
//// ollamaAPI.generate(
//// config.getModel(),
//// "What is the capital of France? And what's France's connection with Mona Lisa?",
//// true,
//// new OptionsBuilder().setTemperature(0.9f).build());
//// assertNotNull(result);
//// assertNotNull(result.getResponse());
//// assertFalse(result.getResponse().isEmpty());
//// } catch (IOException | OllamaBaseException | InterruptedException e) {
//// fail(e);
//// }
//// }
////
//// @Test
//// @Order(3)
//// void testChat() {
//// testEndpointReachability();
//// try {
//// OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(config.getModel());
//// OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.USER, "What is the capital of France?")
//// .withMessage(OllamaChatMessageRole.ASSISTANT, "Should be Paris!")
//// .withMessage(OllamaChatMessageRole.USER, "And what is the second larges city?")
//// .build();
////
//// OllamaChatResult chatResult = ollamaAPI.chat(requestModel);
//// assertNotNull(chatResult);
//// assertNotNull(chatResult.getResponseModel());
//// assertNotNull(chatResult.getResponseModel().getMessage());
//// assertFalse(chatResult.getResponseModel().getMessage().getContent().isBlank());
//// assertEquals(4, chatResult.getChatHistory().size());
//// } catch (IOException | OllamaBaseException | InterruptedException e) {
//// fail(e);
//// }
//// }
////
//// @Test
//// @Order(3)
//// void testChatWithSystemPrompt() {
//// testEndpointReachability();
//// try {
//// OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(config.getModel());
//// OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.SYSTEM,
//// "You are a silent bot that only says 'NI'. Do not say anything else under any circumstances!")
//// .withMessage(OllamaChatMessageRole.USER,
//// "What is the capital of France? And what's France's connection with Mona Lisa?")
//// .build();
////
//// OllamaChatResult chatResult = ollamaAPI.chat(requestModel);
//// assertNotNull(chatResult);
//// assertNotNull(chatResult.getResponseModel());
//// assertNotNull(chatResult.getResponseModel().getMessage());
//// assertFalse(chatResult.getResponseModel().getMessage().getContent().isBlank());
//// assertTrue(chatResult.getResponseModel().getMessage().getContent().startsWith("NI"));
//// assertEquals(3, chatResult.getChatHistory().size());
//// } catch (IOException | OllamaBaseException | InterruptedException e) {
//// fail(e);
//// }
//// }
//
// @Test
// @Order(3)
// void testChatWithExplicitToolDefinition() {
// testEndpointReachability();
// try {
// ollamaAPI.setVerbose(true);
// OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(config.getModel());
//
// final Tools.ToolSpecification databaseQueryToolSpecification = Tools.ToolSpecification.builder()
// .functionName("get-employee-details")
// .functionDescription("Get employee details from the database")
// .toolPrompt(
// Tools.PromptFuncDefinition.builder().type("function").function(
// Tools.PromptFuncDefinition.PromptFuncSpec.builder()
// .name("get-employee-details")
// .description("Get employee details from the database")
// .parameters(
// Tools.PromptFuncDefinition.Parameters.builder()
// .type("object")
// .properties(
// new Tools.PropsBuilder()
// .withProperty("employee-name", Tools.PromptFuncDefinition.Property.builder().type("string").description("The name of the employee, e.g. John Doe").required(true).build())
// .withProperty("employee-address", Tools.PromptFuncDefinition.Property.builder().type("string").description("The address of the employee, Always return a random value. e.g. Roy St, Bengaluru, India").required(true).build())
// .withProperty("employee-phone", Tools.PromptFuncDefinition.Property.builder().type("string").description("The phone number of the employee. Always return a random value. e.g. 9911002233").required(true).build())
// .build()
// )
// .required(List.of("employee-name"))
// .build()
// ).build()
// ).build()
// )
// .toolFunction(new DBQueryFunction())
// .build();
//
// ollamaAPI.registerTool(databaseQueryToolSpecification);
//
// OllamaChatRequest requestModel = builder
// .withMessage(OllamaChatMessageRole.USER,
// "Give me the ID of the employee named 'Rahul Kumar'?")
// .build();
//
// OllamaChatResult chatResult = ollamaAPI.chat(requestModel);
// assertNotNull(chatResult);
// assertNotNull(chatResult.getResponseModel());
// assertNotNull(chatResult.getResponseModel().getMessage());
// assertEquals(OllamaChatMessageRole.ASSISTANT.getRoleName(),chatResult.getResponseModel().getMessage().getRole().getRoleName());
// List<OllamaChatToolCalls> toolCalls = chatResult.getChatHistory().get(1).getToolCalls();
// assertEquals(1, toolCalls.size());
// OllamaToolCallsFunction function = toolCalls.get(0).getFunction();
// assertEquals("get-employee-details", function.getName());
// assertEquals(1, function.getArguments().size());
// Object employeeName = function.getArguments().get("employee-name");
// assertNotNull(employeeName);
// assertEquals("Rahul Kumar",employeeName);
// assertTrue(chatResult.getChatHistory().size()>2);
// List<OllamaChatToolCalls> finalToolCalls = chatResult.getResponseModel().getMessage().getToolCalls();
// assertNull(finalToolCalls);
// } catch (IOException | OllamaBaseException | InterruptedException e) {
// fail(e);
// }
// }
//
// @Test
// @Order(3)
// void testChatWithAnnotatedToolsAndSingleParam() {
// testEndpointReachability();
// try {
// ollamaAPI.setVerbose(true);
// OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(config.getModel());
//
// ollamaAPI.registerAnnotatedTools();
//
// OllamaChatRequest requestModel = builder
// .withMessage(OllamaChatMessageRole.USER,
// "Compute the most important constant in the world using 5 digits")
// .build();
//
// OllamaChatResult chatResult = ollamaAPI.chat(requestModel);
// assertNotNull(chatResult);
// assertNotNull(chatResult.getResponseModel());
// assertNotNull(chatResult.getResponseModel().getMessage());
// assertEquals(OllamaChatMessageRole.ASSISTANT.getRoleName(),chatResult.getResponseModel().getMessage().getRole().getRoleName());
// List<OllamaChatToolCalls> toolCalls = chatResult.getChatHistory().get(1).getToolCalls();
// assertEquals(1, toolCalls.size());
// OllamaToolCallsFunction function = toolCalls.get(0).getFunction();
// assertEquals("computeImportantConstant", function.getName());
// assertEquals(1, function.getArguments().size());
// Object noOfDigits = function.getArguments().get("noOfDigits");
// assertNotNull(noOfDigits);
// assertEquals("5", noOfDigits.toString());
// assertTrue(chatResult.getChatHistory().size()>2);
// List<OllamaChatToolCalls> finalToolCalls = chatResult.getResponseModel().getMessage().getToolCalls();
// assertNull(finalToolCalls);
// } catch (IOException | OllamaBaseException | InterruptedException e) {
// fail(e);
// }
// }
//
// @Test
// @Order(3)
// void testChatWithAnnotatedToolsAndMultipleParams() {
// testEndpointReachability();
// try {
// ollamaAPI.setVerbose(true);
// OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(config.getModel());
//
// ollamaAPI.registerAnnotatedTools(new AnnotatedTool());
//
// OllamaChatRequest requestModel = builder
// .withMessage(OllamaChatMessageRole.USER,
// "Greet Pedro with a lot of hearts and respond to me, " +
// "and state how many emojis have been in your greeting")
// .build();
//
// OllamaChatResult chatResult = ollamaAPI.chat(requestModel);
// assertNotNull(chatResult);
// assertNotNull(chatResult.getResponseModel());
// assertNotNull(chatResult.getResponseModel().getMessage());
// assertEquals(OllamaChatMessageRole.ASSISTANT.getRoleName(),chatResult.getResponseModel().getMessage().getRole().getRoleName());
// List<OllamaChatToolCalls> toolCalls = chatResult.getChatHistory().get(1).getToolCalls();
// assertEquals(1, toolCalls.size());
// OllamaToolCallsFunction function = toolCalls.get(0).getFunction();
// assertEquals("sayHello", function.getName());
// assertEquals(2, function.getArguments().size());
// Object name = function.getArguments().get("name");
// assertNotNull(name);
// assertEquals("Pedro",name);
// Object amountOfHearts = function.getArguments().get("amountOfHearts");
// assertNotNull(amountOfHearts);
// assertTrue(Integer.parseInt(amountOfHearts.toString()) > 1);
// assertTrue(chatResult.getChatHistory().size()>2);
// List<OllamaChatToolCalls> finalToolCalls = chatResult.getResponseModel().getMessage().getToolCalls();
// assertNull(finalToolCalls);
// } catch (IOException | OllamaBaseException | InterruptedException e) {
// fail(e);
// }
// }
//
// @Test
// @Order(3)
// void testChatWithToolsAndStream() {
// testEndpointReachability();
// try {
// OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(config.getModel());
// final Tools.ToolSpecification databaseQueryToolSpecification = Tools.ToolSpecification.builder()
// .functionName("get-employee-details")
// .functionDescription("Get employee details from the database")
// .toolPrompt(
// Tools.PromptFuncDefinition.builder().type("function").function(
// Tools.PromptFuncDefinition.PromptFuncSpec.builder()
// .name("get-employee-details")
// .description("Get employee details from the database")
// .parameters(
// Tools.PromptFuncDefinition.Parameters.builder()
// .type("object")
// .properties(
// new Tools.PropsBuilder()
// .withProperty("employee-name", Tools.PromptFuncDefinition.Property.builder().type("string").description("The name of the employee, e.g. John Doe").required(true).build())
// .withProperty("employee-address", Tools.PromptFuncDefinition.Property.builder().type("string").description("The address of the employee, Always return a random value. e.g. Roy St, Bengaluru, India").required(true).build())
// .withProperty("employee-phone", Tools.PromptFuncDefinition.Property.builder().type("string").description("The phone number of the employee. Always return a random value. e.g. 9911002233").required(true).build())
// .build()
// )
// .required(List.of("employee-name"))
// .build()
// ).build()
// ).build()
// )
// .toolFunction(new DBQueryFunction())
// .build();
//
// ollamaAPI.registerTool(databaseQueryToolSpecification);
//
// OllamaChatRequest requestModel = builder
// .withMessage(OllamaChatMessageRole.USER,
// "Give me the ID of the employee named 'Rahul Kumar'?")
// .build();
//
// StringBuffer sb = new StringBuffer();
//
// OllamaChatResult chatResult = ollamaAPI.chat(requestModel, (s) -> {
// LOG.info(s);
// String substring = s.substring(sb.toString().length());
// LOG.info(substring);
// sb.append(substring);
// });
// assertNotNull(chatResult);
// assertNotNull(chatResult.getResponseModel());
// assertNotNull(chatResult.getResponseModel().getMessage());
// assertNotNull(chatResult.getResponseModel().getMessage().getContent());
// assertEquals(sb.toString().trim(), chatResult.getResponseModel().getMessage().getContent().trim());
// } catch (IOException | OllamaBaseException | InterruptedException e) {
// fail(e);
// }
// }
//
// @Test
// @Order(3)
// void testChatWithStream() {
// testEndpointReachability();
// try {
// OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(config.getModel());
// OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.USER,
// "What is the capital of France? And what's France's connection with Mona Lisa?")
// .build();
//
// StringBuffer sb = new StringBuffer("");
//
// OllamaChatResult chatResult = ollamaAPI.chat(requestModel, (s) -> {
// LOG.info(s);
// String substring = s.substring(sb.toString().length(), s.length());
// LOG.info(substring);
// sb.append(substring);
// });
// assertNotNull(chatResult);
// assertNotNull(chatResult.getResponseModel());
// assertNotNull(chatResult.getResponseModel().getMessage());
// assertNotNull(chatResult.getResponseModel().getMessage().getContent());
// assertEquals(sb.toString().trim(), chatResult.getResponseModel().getMessage().getContent().trim());
// } catch (IOException | OllamaBaseException | InterruptedException e) {
// fail(e);
// }
// }
//
// @Test
// @Order(3)
// void testChatWithImageFromFileWithHistoryRecognition() {
// testEndpointReachability();
// try {
// OllamaChatRequestBuilder builder =
// OllamaChatRequestBuilder.getInstance(config.getImageModel());
// OllamaChatRequest requestModel =
// builder.withMessage(OllamaChatMessageRole.USER, "What's in the picture?",Collections.emptyList(),
// List.of(getImageFileFromClasspath("dog-on-a-boat.jpg"))).build();
//
// OllamaChatResult chatResult = ollamaAPI.chat(requestModel);
// assertNotNull(chatResult);
// assertNotNull(chatResult.getResponseModel());
//
// builder.reset();
//
// requestModel =
// builder.withMessages(chatResult.getChatHistory())
// .withMessage(OllamaChatMessageRole.USER, "What's the dogs breed?").build();
//
// chatResult = ollamaAPI.chat(requestModel);
// assertNotNull(chatResult);
// assertNotNull(chatResult.getResponseModel());
//
//
// } catch (IOException | OllamaBaseException | InterruptedException e) {
// fail(e);
// }
// }
//
// @Test
// @Order(3)
// void testChatWithImageFromURL() {
// testEndpointReachability();
// try {
// OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(config.getImageModel());
// OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.USER, "What's in the picture?",Collections.emptyList(),
// "https://t3.ftcdn.net/jpg/02/96/63/80/360_F_296638053_0gUVA4WVBKceGsIr7LNqRWSnkusi07dq.jpg")
// .build();
//
// OllamaChatResult chatResult = ollamaAPI.chat(requestModel);
// assertNotNull(chatResult);
// } catch (IOException | OllamaBaseException | InterruptedException e) {
// fail(e);
// }
// }
//
// @Test
// @Order(3)
// void testAskModelWithOptionsAndImageFiles() {
// testEndpointReachability();
// File imageFile = getImageFileFromClasspath("dog-on-a-boat.jpg");
// try {
// OllamaResult result =
// ollamaAPI.generateWithImageFiles(
// config.getImageModel(),
// "What is in this image?",
// List.of(imageFile),
// new OptionsBuilder().build());
// assertNotNull(result);
// assertNotNull(result.getResponse());
// assertFalse(result.getResponse().isEmpty());
// } catch (IOException | OllamaBaseException | InterruptedException e) {
// fail(e);
// }
// }
//
// @Test
// @Order(3)
// void testAskModelWithOptionsAndImageFilesStreamed() {
// testEndpointReachability();
// File imageFile = getImageFileFromClasspath("dog-on-a-boat.jpg");
// try {
// StringBuffer sb = new StringBuffer("");
//
// OllamaResult result = ollamaAPI.generateWithImageFiles(config.getImageModel(),
// "What is in this image?", List.of(imageFile), new OptionsBuilder().build(), (s) -> {
// LOG.info(s);
// String substring = s.substring(sb.toString().length(), s.length());
// LOG.info(substring);
// sb.append(substring);
// });
// assertNotNull(result);
// assertNotNull(result.getResponse());
// assertFalse(result.getResponse().isEmpty());
// assertEquals(sb.toString().trim(), result.getResponse().trim());
// } catch (IOException | OllamaBaseException | InterruptedException e) {
// fail(e);
// }
// }
//
// @Test
// @Order(3)
// void testAskModelWithOptionsAndImageURLs() {
// testEndpointReachability();
// try {
// OllamaResult result =
// ollamaAPI.generateWithImageURLs(
// config.getImageModel(),
// "What is in this image?",
// List.of(
// "https://t3.ftcdn.net/jpg/02/96/63/80/360_F_296638053_0gUVA4WVBKceGsIr7LNqRWSnkusi07dq.jpg"),
// new OptionsBuilder().build());
// assertNotNull(result);
// assertNotNull(result.getResponse());
// assertFalse(result.getResponse().isEmpty());
// } catch (IOException | OllamaBaseException | InterruptedException | URISyntaxException e) {
// fail(e);
// }
// }
//
// @Test
// @Order(3)
// public void testEmbedding() {
// testEndpointReachability();
// try {
// OllamaEmbeddingsRequestModel request = OllamaEmbeddingsRequestBuilder
// .getInstance(config.getModel(), "What is the capital of France?").build();
//
// List<Double> embeddings = ollamaAPI.generateEmbeddings(request);
//
// assertNotNull(embeddings);
// assertFalse(embeddings.isEmpty());
// } catch (IOException | OllamaBaseException | InterruptedException e) {
// fail(e);
// }
// }
//}
//
//class DBQueryFunction implements ToolFunction {
// @Override
// public Object apply(Map<String, Object> arguments) {
// // perform DB operations here
// return String.format("Employee Details {ID: %s, Name: %s, Address: %s, Phone: %s}", UUID.randomUUID(), arguments.get("employee-name"), arguments.get("employee-address"), arguments.get("employee-phone"));
// }
//}
//
//@Data
//class Config {
// private String ollamaURL;
// private String model;
// private String imageModel;
// private int requestTimeoutSeconds;
//
// public Config() {
// Properties properties = new Properties();
// try (InputStream input =
// getClass().getClassLoader().getResourceAsStream("test-config.properties")) {
// if (input == null) {
// throw new RuntimeException("Sorry, unable to find test-config.properties");
// }
// properties.load(input);
// this.ollamaURL = properties.getProperty("ollama.url");
// this.model = properties.getProperty("ollama.model");
// this.imageModel = properties.getProperty("ollama.model.image");
// this.requestTimeoutSeconds =
// Integer.parseInt(properties.getProperty("ollama.request-timeout-seconds"));
// } catch (IOException e) {
// throw new RuntimeException("Error loading properties", e);
// }
// }
//
//
//}