diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml
index f9e9277..758078d 100644
--- a/.github/workflows/build-on-pull-request.yml
+++ b/.github/workflows/build-on-pull-request.yml
@@ -20,13 +20,17 @@ jobs:
permissions:
contents: read
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+
steps:
- uses: actions/checkout@v5
- - name: Set up JDK 11
+ - name: Set up JDK 21
uses: actions/setup-java@v5
with:
- java-version: '11'
- distribution: 'adopt-hotspot'
+ java-version: '21'
+ distribution: 'oracle'
server-id: github
settings-path: ${{ github.workspace }}
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index ad52d52..19f5c6e 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -28,8 +28,8 @@ jobs:
if: matrix.language == 'java'
uses: actions/setup-java@v5
with:
- distribution: temurin
- java-version: '11'
+ distribution: oracle
+ java-version: '21'
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
diff --git a/.github/workflows/gh-mvn-publish.yml b/.github/workflows/gh-mvn-publish.yml
index d85b321..6d77e85 100644
--- a/.github/workflows/gh-mvn-publish.yml
+++ b/.github/workflows/gh-mvn-publish.yml
@@ -14,11 +14,11 @@ jobs:
steps:
- uses: actions/checkout@v5
- - name: Set up JDK 17
+ - name: Set up JDK 21
uses: actions/setup-java@v5
with:
- java-version: '17'
- distribution: 'temurin'
+ java-version: '21'
+ distribution: 'oracle'
server-id: github
settings-path: ${{ github.workspace }}
diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml
index 9dba04d..b6aa79a 100644
--- a/.github/workflows/maven-publish.yml
+++ b/.github/workflows/maven-publish.yml
@@ -26,11 +26,11 @@ jobs:
steps:
- uses: actions/checkout@v5
- - name: Set up JDK 17
+ - name: Set up JDK 21
uses: actions/setup-java@v5
with:
- java-version: '17'
- distribution: 'temurin'
+ java-version: '21'
+ distribution: 'oracle'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml
index 50ec9df..7aab5ff 100644
--- a/.github/workflows/publish-docs.yml
+++ b/.github/workflows/publish-docs.yml
@@ -30,11 +30,11 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v5
- - name: Set up JDK 11
+ - name: Set up JDK 21
uses: actions/setup-java@v5
with:
- java-version: '11'
- distribution: 'adopt-hotspot'
+ java-version: '21'
+ distribution: 'oracle'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
index 44069fa..4583cb9 100644
--- a/.github/workflows/run-tests.yml
+++ b/.github/workflows/run-tests.yml
@@ -36,11 +36,11 @@ jobs:
run: |
curl -fsSL https://ollama.com/install.sh | sh
- - name: Set up JDK 17
+ - name: Set up JDK 21
uses: actions/setup-java@v5
with:
- java-version: '17'
- distribution: 'temurin'
+ java-version: '21'
+ distribution: 'oracle'
server-id: github
settings-path: ${{ github.workspace }}
diff --git a/.gitignore b/.gitignore
index 788123e..0c97cfe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,4 +41,4 @@ pom.xml.*
release.properties
!.idea/icon.svg
-src/main/java/io/github/ollama4j/localtests
\ No newline at end of file
+src/main/java/io/github/ollama4j/localtests
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 7c1bf5c..94d3f75 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -21,11 +21,19 @@ repos:
# for commit message formatting
- repo: https://github.com/commitizen-tools/commitizen
- rev: v4.9.0
+ rev: v4.9.1
hooks:
- id: commitizen
stages: [commit-msg]
+ - repo: local
+ hooks:
+ - id: format-code
+ name: Format Code
+ entry: make apply-formatting
+ language: system
+ always_run: true
+
# # for java code quality
# - repo: https://github.com/gherynos/pre-commit-java
# rev: v0.6.10
diff --git a/LICENSE b/LICENSE
index 85c8a43..883ee94 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2023 Amith Koujalgi
+Copyright (c) 2023 Amith Koujalgi and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/Makefile b/Makefile
index 2753d0e..7b5ad0c 100644
--- a/Makefile
+++ b/Makefile
@@ -2,41 +2,70 @@ 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
+ @pre-commit install
+ @pre-commit autoupdate
+ @pre-commit install --install-hooks
-build:
- mvn -B clean install -Dgpg.skip=true
+check-formatting:
+ @echo "\033[0;34mChecking code formatting...\033[0m"
+ @mvn spotless:check
-full-build:
- mvn -B clean install
+apply-formatting:
+ @echo "\033[0;32mApplying code formatting...\033[0m"
+ @mvn spotless:apply
-unit-tests:
- mvn clean test -Punit-tests
+build: apply-formatting
+ @echo "\033[0;34mBuilding project (GPG skipped)...\033[0m"
+ @mvn -B clean install -Dgpg.skip=true -Dmaven.javadoc.skip=true
-integration-tests:
- export USE_EXTERNAL_OLLAMA_HOST=false && mvn clean verify -Pintegration-tests
+full-build: apply-formatting
+ @echo "\033[0;34mPerforming full build...\033[0m"
+ @mvn -B clean install
-integration-tests-remote:
- export USE_EXTERNAL_OLLAMA_HOST=true && export OLLAMA_HOST=http://192.168.29.223:11434 && mvn clean verify -Pintegration-tests -Dgpg.skip=true
+unit-tests: apply-formatting
+ @echo "\033[0;34mRunning unit tests...\033[0m"
+ @mvn clean test -Punit-tests
+
+integration-tests: apply-formatting
+ @echo "\033[0;34mRunning integration tests (local)...\033[0m"
+ @export USE_EXTERNAL_OLLAMA_HOST=false && mvn clean verify -Pintegration-tests
+
+integration-tests-remote: apply-formatting
+ @echo "\033[0;34mRunning integration tests (remote)...\033[0m"
+ @export USE_EXTERNAL_OLLAMA_HOST=true && export OLLAMA_HOST=http://192.168.29.229:11434 && mvn clean verify -Pintegration-tests -Dgpg.skip=true
doxygen:
- doxygen Doxyfile
+ @echo "\033[0;34mGenerating documentation with Doxygen...\033[0m"
+ @doxygen Doxyfile
+
+javadoc:
+ @echo "\033[0;34mGenerating Javadocs into '$(javadocfolder)'...\033[0m"
+ @mvn clean javadoc:javadoc
+ @if [ -f "target/reports/apidocs/index.html" ]; then \
+ echo "\033[0;32mJavadocs generated in target/reports/apidocs/index.html\033[0m"; \
+ else \
+ echo "\033[0;31mFailed to generate Javadocs in target/reports/apidocs\033[0m"; \
+ exit 1; \
+ fi
list-releases:
- curl 'https://central.sonatype.com/api/internal/browse/component/versions?sortField=normalizedVersion&sortDirection=desc&page=0&size=20&filter=namespace%3Aio.github.ollama4j%2Cname%3Aollama4j' \
+ @echo "\033[0;34mListing latest releases...\033[0m"
+ @curl 'https://central.sonatype.com/api/internal/browse/component/versions?sortField=normalizedVersion&sortDirection=desc&page=0&size=20&filter=namespace%3Aio.github.ollama4j%2Cname%3Aollama4j' \
--compressed \
--silent | jq -r '.components[].version'
docs-build:
- npm i --prefix docs && npm run build --prefix docs
+ @echo "\033[0;34mBuilding documentation site...\033[0m"
+ @cd ./docs && npm ci --no-audit --fund=false && npm run build
docs-serve:
- npm i --prefix docs && npm run start --prefix docs
+ @echo "\033[0;34mServing documentation site...\033[0m"
+ @cd ./docs && npm install && npm run start
start-cpu:
- docker run -it -v ~/ollama:/root/.ollama -p 11434:11434 ollama/ollama
+ @echo "\033[0;34mStarting Ollama (CPU mode)...\033[0m"
+ @docker run -it -v ~/ollama:/root/.ollama -p 11434:11434 ollama/ollama
start-gpu:
- docker run -it --gpus=all -v ~/ollama:/root/.ollama -p 11434:11434 ollama/ollama
\ No newline at end of file
+ @echo "\033[0;34mStarting Ollama (GPU mode)...\033[0m"
+ @docker run -it --gpus=all -v ~/ollama:/root/.ollama -p 11434:11434 ollama/ollama
\ No newline at end of file
diff --git a/README.md b/README.md
index 37e35fc..19a7e7d 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,8 @@
- ### Ollama4j
+### Ollama4j
+
@@ -40,15 +41,52 @@ _Find more details on the **[website](https://ollama4j.github.io/ollama4j/)**._
## Table of Contents
+- [Capabilities](#capabilities)
- [How does it work?](#how-does-it-work)
- [Requirements](#requirements)
-- [Installation](#installation)
-- [API Spec](https://ollama4j.github.io/ollama4j/category/apis---model-management)
+- [Usage](#usage)
+ - [For Maven](#for-maven)
+ - [Using Maven Central](#using-maven-central)
+ - [Using GitHub's Maven Package Repository](#using-githubs-maven-package-repository)
+ - [For Gradle](#for-gradle)
+- [API Spec](#api-spec)
- [Examples](#examples)
-- [Javadoc](https://ollama4j.github.io/ollama4j/apidocs/)
- [Development](#development)
-- [Contributions](#get-involved)
-- [References](#references)
+ - [Setup dev environment](#setup-dev-environment)
+ - [Build](#build)
+ - [Run unit tests](#run-unit-tests)
+ - [Run integration tests](#run-integration-tests)
+ - [Releases](#releases)
+- [Get Involved](#get-involved)
+- [Who's using Ollama4j?](#whos-using-ollama4j)
+- [Growth](#growth)
+ - [References](#references)
+ - [Credits](#credits)
+ - [Appreciate the work?](#appreciate-the-work)
+
+## Capabilities
+
+- **Text generation**: Single-turn `generate` with optional streaming and advanced options
+- **Chat**: Multi-turn chat with conversation history and roles
+- **Tool/function calling**: Built-in tool invocation via annotations and tool specs
+- **Reasoning/thinking modes**: Generate and chat with “thinking” outputs where supported
+- **Image inputs (multimodal)**: Generate with images as inputs where models support vision
+- **Embeddings**: Create vector embeddings for text
+- **Async generation**: Fire-and-forget style generation APIs
+- **Custom roles**: Define and use custom chat roles
+- **Model management**: List, pull, create, delete, and get model details
+- **Connectivity utilities**: Server `ping` and process status (`ps`)
+- **Authentication**: Basic auth and bearer token support
+- **Options builder**: Type-safe builder for model parameters and request options
+- **Timeouts**: Configure connect/read/write timeouts
+- **Logging**: Built-in logging hooks for requests and responses
+- **Metrics & Monitoring** 🆕: Built-in Prometheus metrics export for real-time monitoring of requests, model usage, and
+ performance. *(Beta feature – feedback/contributions welcome!)* -
+ Checkout [ollama4j-examples](https://github.com/ollama4j/ollama4j-examples) repository for details.
+
+
+
+
## How does it work?
@@ -73,7 +111,7 @@ _Find more details on the **[website](https://ollama4j.github.io/ollama4j/)**._
-## Installation
+## Usage
> [!NOTE]
> We are now publishing the artifacts to both Maven Central and GitHub package repositories.
@@ -182,7 +220,7 @@ dependencies {
[lib-shield]: https://img.shields.io/badge/ollama4j-get_latest_version-blue.svg?style=just-the-message&labelColor=gray
-#### API Spec
+### API Spec
> [!TIP]
> Find the full API specifications on the [website](https://ollama4j.github.io/ollama4j/).
diff --git a/docs/METRICS.md b/docs/METRICS.md
new file mode 100644
index 0000000..9261a99
--- /dev/null
+++ b/docs/METRICS.md
@@ -0,0 +1,184 @@
+# Prometheus Metrics Integration
+
+Ollama4j now includes comprehensive Prometheus metrics collection to help you monitor and observe your Ollama API usage. This feature allows you to track request counts, response times, model usage, and other operational metrics.
+
+## Features
+
+The metrics integration provides the following metrics:
+
+- **Request Metrics**: Total requests, duration histograms, and response time summaries by endpoint
+- **Model Usage**: Model-specific usage statistics and response times
+- **Token Generation**: Token count tracking per model
+- **Error Tracking**: Error counts by type and endpoint
+- **Active Connections**: Current number of active API connections
+
+## Quick Start
+
+### 1. Enable Metrics Collection
+
+```java
+import io.github.ollama4j.OllamaAPI;
+
+// Create API instance with metrics enabled
+OllamaAPI ollamaAPI = new OllamaAPI();
+ollamaAPI.setMetricsEnabled(true);
+```
+
+### 2. Start Metrics Server
+
+```java
+import io.prometheus.client.exporter.HTTPServer;
+
+// Start Prometheus metrics HTTP server on port 8080
+HTTPServer metricsServer = new HTTPServer(8080);
+System.out.println("Metrics available at: http://localhost:8080/metrics");
+```
+
+### 3. Use the API (Metrics are automatically collected)
+
+```java
+// All API calls are automatically instrumented
+boolean isReachable = ollamaAPI.ping();
+
+Map format = new HashMap<>();
+format.put("type", "json");
+OllamaResult result = ollamaAPI.generateWithFormat(
+ "llama2",
+ "Generate a JSON object",
+ format
+);
+```
+
+## Available Metrics
+
+### Request Metrics
+
+- `ollama_api_requests_total` - Total number of API requests by endpoint, method, and status
+- `ollama_api_request_duration_seconds` - Request duration histogram by endpoint and method
+- `ollama_api_response_time_seconds` - Response time summary with percentiles
+
+### Model Metrics
+
+- `ollama_model_usage_total` - Model usage count by model name and operation
+- `ollama_model_response_time_seconds` - Model response time histogram
+- `ollama_tokens_generated_total` - Total tokens generated by model
+
+### System Metrics
+
+- `ollama_api_active_connections` - Current number of active connections
+- `ollama_api_errors_total` - Error count by endpoint and error type
+
+## Example Metrics Output
+
+```
+# HELP ollama_api_requests_total Total number of Ollama API requests
+# TYPE ollama_api_requests_total counter
+ollama_api_requests_total{endpoint="/api/generate",method="POST",status="success"} 5.0
+ollama_api_requests_total{endpoint="/api/embed",method="POST",status="success"} 3.0
+
+# HELP ollama_api_request_duration_seconds Duration of Ollama API requests in seconds
+# TYPE ollama_api_request_duration_seconds histogram
+ollama_api_request_duration_seconds_bucket{endpoint="/api/generate",method="POST",le="0.1"} 0.0
+ollama_api_request_duration_seconds_bucket{endpoint="/api/generate",method="POST",le="0.5"} 2.0
+ollama_api_request_duration_seconds_bucket{endpoint="/api/generate",method="POST",le="1.0"} 4.0
+ollama_api_request_duration_seconds_bucket{endpoint="/api/generate",method="POST",le="+Inf"} 5.0
+ollama_api_request_duration_seconds_sum{endpoint="/api/generate",method="POST"} 2.5
+ollama_api_request_duration_seconds_count{endpoint="/api/generate",method="POST"} 5.0
+
+# HELP ollama_model_usage_total Total number of model usage requests
+# TYPE ollama_model_usage_total counter
+ollama_model_usage_total{model_name="llama2",operation="generate_with_format"} 5.0
+ollama_model_usage_total{model_name="llama2",operation="embed"} 3.0
+
+# HELP ollama_tokens_generated_total Total number of tokens generated
+# TYPE ollama_tokens_generated_total counter
+ollama_tokens_generated_total{model_name="llama2"} 150.0
+```
+
+## Configuration
+
+### Enable/Disable Metrics
+
+```java
+OllamaAPI ollamaAPI = new OllamaAPI();
+
+// Enable metrics collection
+ollamaAPI.setMetricsEnabled(true);
+
+// Disable metrics collection (default)
+ollamaAPI.setMetricsEnabled(false);
+```
+
+### Custom Metrics Server
+
+```java
+import io.prometheus.client.exporter.HTTPServer;
+
+// Start on custom port
+HTTPServer metricsServer = new HTTPServer(9090);
+
+// Start on custom host and port
+HTTPServer metricsServer = new HTTPServer("0.0.0.0", 9090);
+```
+
+## Integration with Prometheus
+
+### Prometheus Configuration
+
+Add this to your `prometheus.yml`:
+
+```yaml
+scrape_configs:
+ - job_name: 'ollama4j'
+ static_configs:
+ - targets: ['localhost:8080']
+ scrape_interval: 15s
+```
+
+### Grafana Dashboards
+
+You can create Grafana dashboards using the metrics. Some useful queries:
+
+- **Request Rate**: `rate(ollama_api_requests_total[5m])`
+- **Average Response Time**: `rate(ollama_api_request_duration_seconds_sum[5m]) / rate(ollama_api_request_duration_seconds_count[5m])`
+- **Error Rate**: `rate(ollama_api_requests_total{status="error"}[5m]) / rate(ollama_api_requests_total[5m])`
+- **Model Usage**: `rate(ollama_model_usage_total[5m])`
+- **Token Generation Rate**: `rate(ollama_tokens_generated_total[5m])`
+
+## Performance Considerations
+
+- Metrics collection adds minimal overhead (~1-2% in most cases)
+- Metrics are collected asynchronously and don't block API calls
+- You can disable metrics in production if needed: `ollamaAPI.setMetricsEnabled(false)`
+- The metrics server uses minimal resources
+
+## Troubleshooting
+
+### Metrics Not Appearing
+
+1. Ensure metrics are enabled: `ollamaAPI.setMetricsEnabled(true)`
+2. Check that the metrics server is running: `http://localhost:8080/metrics`
+3. Verify API calls are being made (metrics only appear after API usage)
+
+### High Memory Usage
+
+- Metrics accumulate over time. Consider restarting your application periodically
+- Use Prometheus to scrape metrics regularly to avoid accumulation
+
+### Custom Metrics
+
+You can extend the metrics by accessing the Prometheus registry directly:
+
+```java
+import io.prometheus.client.CollectorRegistry;
+import io.prometheus.client.Counter;
+
+// Create custom metrics
+Counter customCounter = Counter.build()
+ .name("my_custom_metric_total")
+ .help("My custom metric")
+ .register();
+
+// Use the metric
+customCounter.inc();
+```
diff --git a/docs/blog/2025-03-08-blog/index.md b/docs/blog/2025-03-08-blog/index.md
index b702f39..347ed86 100644
--- a/docs/blog/2025-03-08-blog/index.md
+++ b/docs/blog/2025-03-08-blog/index.md
@@ -337,7 +337,7 @@ import com.couchbase.client.java.Scope;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.query.QueryResult;
import io.github.ollama4j.OllamaAPI;
-import io.github.ollama4j.exceptions.OllamaBaseException;
+import io.github.ollama4j.exceptions.OllamaException;
import io.github.ollama4j.exceptions.ToolInvocationException;
import io.github.ollama4j.tools.OllamaToolsResult;
import io.github.ollama4j.tools.ToolFunction;
@@ -356,7 +356,7 @@ import java.util.Map;
public class CouchbaseToolCallingExample {
- public static void main(String[] args) throws IOException, ToolInvocationException, OllamaBaseException, InterruptedException {
+ public static void main(String[] args) throws IOException, ToolInvocationException, OllamaException, InterruptedException {
String connectionString = Utilities.getFromEnvVar("CB_CLUSTER_URL");
String username = Utilities.getFromEnvVar("CB_CLUSTER_USERNAME");
String password = Utilities.getFromEnvVar("CB_CLUSTER_PASSWORD");
diff --git a/docs/docs/apis-extras/basic-auth.md b/docs/docs/apis-extras/basic-auth.md
index 15f681c..1e96177 100644
--- a/docs/docs/apis-extras/basic-auth.md
+++ b/docs/docs/apis-extras/basic-auth.md
@@ -1,8 +1,8 @@
---
-sidebar_position: 2
+sidebar_position: 3
---
-# Set Basic Authentication
+# Basic Auth
This API lets you set the basic authentication for the Ollama client. This would help in scenarios where
Ollama server would be setup behind a gateway/reverse proxy with basic auth.
diff --git a/docs/docs/apis-extras/bearer-auth.md b/docs/docs/apis-extras/bearer-auth.md
index 1ae3e80..cdd4b3a 100644
--- a/docs/docs/apis-extras/bearer-auth.md
+++ b/docs/docs/apis-extras/bearer-auth.md
@@ -1,8 +1,8 @@
---
-sidebar_position: 2
+sidebar_position: 4
---
-# Set Bearer Authentication
+# Bearer Auth
This API lets you set the bearer authentication for the Ollama client. This would help in scenarios where
Ollama server would be setup behind a gateway/reverse proxy with bearer auth.
diff --git a/docs/docs/apis-extras/logging.md b/docs/docs/apis-extras/logging.md
new file mode 100644
index 0000000..d73ba10
--- /dev/null
+++ b/docs/docs/apis-extras/logging.md
@@ -0,0 +1,26 @@
+---
+sidebar_position: 7
+---
+
+# Logging
+
+### Using with SLF4J and Logback
+
+Add a `logback.xml` file to your `src/main/resources` folder with the following content:
+
+```xml
+
+
+
+
+
+
+
+
+ %d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n
+
+
+
+
+
+```
\ No newline at end of file
diff --git a/docs/docs/apis-extras/ping.md b/docs/docs/apis-extras/ping.md
index b52fff0..256c26b 100644
--- a/docs/docs/apis-extras/ping.md
+++ b/docs/docs/apis-extras/ping.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 3
+sidebar_position: 5
---
# Ping
diff --git a/docs/docs/apis-generate/prompt-builder.md b/docs/docs/apis-extras/prompt-builder.md
similarity index 98%
rename from docs/docs/apis-generate/prompt-builder.md
rename to docs/docs/apis-extras/prompt-builder.md
index dfbd6a8..3240591 100644
--- a/docs/docs/apis-generate/prompt-builder.md
+++ b/docs/docs/apis-extras/prompt-builder.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 10
+sidebar_position: 2
---
# Prompt Builder
@@ -51,6 +51,7 @@ public class Main {
You will get a response similar to:
+:::tip[LLM Response]
```go
package main
@@ -71,4 +72,5 @@ func readFile(fileName string) {
fmt.Println(f.String())
}
}
-```
\ No newline at end of file
+```
+:::
\ No newline at end of file
diff --git a/docs/docs/apis-extras/ps.md b/docs/docs/apis-extras/ps.md
index 4f37e04..43b0af5 100644
--- a/docs/docs/apis-extras/ps.md
+++ b/docs/docs/apis-extras/ps.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 4
+sidebar_position: 5
---
# PS
@@ -12,17 +12,14 @@ This API corresponds to the [PS](https://github.com/ollama/ollama/blob/main/docs
package io.github.ollama4j.localtests;
import io.github.ollama4j.OllamaAPI;
-import io.github.ollama4j.exceptions.OllamaBaseException;
-import io.github.ollama4j.models.ps.ModelsProcessResponse;
-
-import java.io.IOException;
+import io.github.ollama4j.models.ps.ModelProcessesResult;
public class Main {
public static void main(String[] args) {
OllamaAPI ollamaAPI = new OllamaAPI("http://localhost:11434");
- ModelsProcessResponse response = ollamaAPI.ps();
+ ModelProcessesResult response = ollamaAPI.ps();
System.out.println(response);
}
diff --git a/docs/docs/apis-extras/request-timeout.md b/docs/docs/apis-extras/timeouts.md
similarity index 90%
rename from docs/docs/apis-extras/request-timeout.md
rename to docs/docs/apis-extras/timeouts.md
index f22971a..2b0b52c 100644
--- a/docs/docs/apis-extras/request-timeout.md
+++ b/docs/docs/apis-extras/timeouts.md
@@ -2,7 +2,9 @@
sidebar_position: 2
---
-# Set Request Timeout
+# Timeouts
+
+### Set Request Timeout
This API lets you set the request timeout for the Ollama client.
diff --git a/docs/docs/apis-generate/chat-with-tools.md b/docs/docs/apis-generate/chat-with-tools.md
index edc1dc4..eca5e15 100644
--- a/docs/docs/apis-generate/chat-with-tools.md
+++ b/docs/docs/apis-generate/chat-with-tools.md
@@ -21,25 +21,25 @@ session. The tool invocation and response handling are all managed internally by
-::::tip[LLM Response]
+:::tip[LLM Response]
**First answer:** 6527fb60-9663-4073-b59e-855526e0a0c2 is the ID of the employee named 'Rahul Kumar'.
**Second answer:** _Kumar_ is the last name of the employee named 'Rahul Kumar'.
-::::
+:::
This tool calling can also be done using the streaming API.
-### Client-managed tool calls (clientHandlesTools)
+### Client-managed tool calls (useTools)
By default, ollama4j automatically executes tool calls returned by the model during chat, runs the corresponding registered Java methods, and appends the tool results back into the conversation. For some applications, you may want to intercept tool calls and decide yourself when and how to execute them (for example, to queue them, to show a confirmation UI to the user, to run them in a sandbox, or to perform multi‑step orchestration).
-To enable this behavior, set the clientHandlesTools flag to true on your OllamaAPI instance. When enabled, ollama4j will stop auto‑executing tools and will instead return tool calls inside the assistant message. You can then inspect the tool calls and execute them manually.
+To enable this behavior, set the useTools flag to true on your OllamaAPI instance. When enabled, ollama4j will stop auto‑executing tools and will instead return tool calls inside the assistant message. You can then inspect the tool calls and execute them manually.
Notes:
-- Default value: clientHandlesTools is false for backward compatibility.
-- When clientHandlesTools is false, ollama4j auto‑executes tools and loops internally until tools are resolved or max retries is reached.
-- When clientHandlesTools is true, ollama4j will not execute tools; you are responsible for invoking tools and passing results back as TOOL messages, then re‑calling chat() to continue.
+- Default value: useTools is true.
+- When useTools is false, ollama4j auto‑executes tools and loops internally until tools are resolved or max retries is reached.
+- When useTools is true, ollama4j will not execute tools; you are responsible for invoking tools and passing results back as TOOL messages, then re‑calling chat() to continue.
### Annotation-Based Tool Registration
@@ -74,8 +74,8 @@ The annotated method can then be used as a tool in the chat session:
Running the above would produce a response similar to:
-::::tip[LLM Response]
+:::tip[LLM Response]
**First answer:** 0.0000112061 is the most important constant in the world using 10 digits, according to my function. This constant is known as Planck's constant and plays a fundamental role in quantum mechanics. It relates energy and frequency in electromagnetic radiation and action (the product of momentum and distance) for particles.
**Second answer:** 3-digit constant: 8.001
-::::
+:::
diff --git a/docs/docs/apis-generate/chat.md b/docs/docs/apis-generate/chat.md
index 08087b0..af53342 100644
--- a/docs/docs/apis-generate/chat.md
+++ b/docs/docs/apis-generate/chat.md
@@ -16,7 +16,7 @@ information using the history of already asked questions and the respective answ
You will get a response similar to:
-::::tip[LLM Response]
+:::tip[LLM Response]
> First answer: The capital of France is Paris.
>
@@ -47,7 +47,7 @@ You will get a response similar to:
"tool_calls" : null
}]
```
-::::
+:::
### Create a conversation where the answer is streamed
@@ -75,9 +75,9 @@ You will get a response similar to:
You will get a response as:
-::::tip[LLM Response]
+:::tip[LLM Response]
Shhh!
-::::
+:::
## Create a conversation about an image (requires a vision model)
@@ -91,7 +91,7 @@ Let's use this image:
You will get a response similar to:
-::::tip[LLM Response]
+:::tip[LLM Response]
**First Answer:** The image shows a dog sitting on the bow of a boat that is docked in calm water. The boat has two
levels, with the lower level containing seating and what appears to be an engine cover. The dog seems relaxed and
comfortable on the boat, looking out over the water. The background suggests it might be late afternoon or early
@@ -101,4 +101,4 @@ evening, given the warm lighting and the low position of the sun in the sky.
appears to be medium-sized with a short coat and a brown coloration, which might suggest that it is a **_Golden Retriever_**
or a similar breed. Without more details like ear shape and tail length, it's not possible to identify the exact breed
confidently.
-::::
+:::
diff --git a/docs/docs/apis-generate/generate-async.md b/docs/docs/apis-generate/generate-async.md
index a2eb5af..fe659ce 100644
--- a/docs/docs/apis-generate/generate-async.md
+++ b/docs/docs/apis-generate/generate-async.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 2
+sidebar_position: 6
---
import CodeEmbed from '@site/src/components/CodeEmbed';
diff --git a/docs/docs/apis-generate/generate-embeddings.md b/docs/docs/apis-generate/generate-embeddings.md
index f716feb..152c8da 100644
--- a/docs/docs/apis-generate/generate-embeddings.md
+++ b/docs/docs/apis-generate/generate-embeddings.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 5
+sidebar_position: 1
---
import CodeEmbed from '@site/src/components/CodeEmbed';
@@ -12,7 +12,7 @@ Generate embeddings from a model.
-::::tip[LLM Response]
+:::tip[LLM Response]
```json
[
@@ -40,7 +40,7 @@ Generate embeddings from a model.
]
```
-::::
+:::
You could also use the `OllamaEmbedRequestModel` to specify the options such as `seed`, `temperature`, etc., to apply
for generating embeddings.
@@ -49,7 +49,7 @@ for generating embeddings.
You will get a response similar to:
-::::tip[LLM Response]
+:::tip[LLM Response]
```json
[
@@ -77,4 +77,4 @@ You will get a response similar to:
]
```
-::::
\ No newline at end of file
+:::
\ No newline at end of file
diff --git a/docs/docs/apis-generate/generate-thinking.md b/docs/docs/apis-generate/generate-thinking.md
index 93a8dea..d38634d 100644
--- a/docs/docs/apis-generate/generate-thinking.md
+++ b/docs/docs/apis-generate/generate-thinking.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 2
+sidebar_position: 3
---
import CodeEmbed from '@site/src/components/CodeEmbed';
diff --git a/docs/docs/apis-generate/generate-with-image-urls.md b/docs/docs/apis-generate/generate-with-image-urls.md
deleted file mode 100644
index cc89e5d..0000000
--- a/docs/docs/apis-generate/generate-with-image-urls.md
+++ /dev/null
@@ -1,33 +0,0 @@
----
-sidebar_position: 4
----
-
-import CodeEmbed from '@site/src/components/CodeEmbed';
-
-# Generate with Image URLs
-
-This API lets you ask questions along with the image files to the LLMs.
-This API corresponds to
-the [completion](https://github.com/jmorganca/ollama/blob/main/docs/api.md#generate-a-completion) API.
-
-:::note
-
-Executing this on Ollama server running in CPU-mode will take longer to generate response. Hence, GPU-mode is
-recommended.
-
-:::
-
-## Ask (Sync)
-
-Passing the link of this image the following code:
-
-
-
-
-
-You will get a response similar to:
-
-::::tip[LLM Response]
-This image features a white boat with brown cushions, where a dog is sitting on the back of the boat. The dog seems to
-be enjoying its time outdoors, perhaps on a lake.
-::::
\ No newline at end of file
diff --git a/docs/docs/apis-generate/generate-with-image-files.md b/docs/docs/apis-generate/generate-with-images.md
similarity index 50%
rename from docs/docs/apis-generate/generate-with-image-files.md
rename to docs/docs/apis-generate/generate-with-images.md
index e17888d..7d1a492 100644
--- a/docs/docs/apis-generate/generate-with-image-files.md
+++ b/docs/docs/apis-generate/generate-with-images.md
@@ -1,10 +1,10 @@
---
-sidebar_position: 3
+sidebar_position: 4
---
import CodeEmbed from '@site/src/components/CodeEmbed';
-# Generate with Image Files
+# Generate with Images
This API lets you ask questions along with the image files to the LLMs.
This API corresponds to
@@ -27,7 +27,35 @@ If you have this image downloaded and you pass the path to the downloaded image
You will get a response similar to:
-::::tip[LLM Response]
+:::tip[LLM Response]
This image features a white boat with brown cushions, where a dog is sitting on the back of the boat. The dog seems to
be enjoying its time outdoors, perhaps on a lake.
-::::
\ No newline at end of file
+:::
+
+# Generate with Image URLs
+
+This API lets you ask questions along with the image files to the LLMs.
+This API corresponds to
+the [completion](https://github.com/jmorganca/ollama/blob/main/docs/api.md#generate-a-completion) API.
+
+:::note
+
+Executing this on Ollama server running in CPU-mode will take longer to generate response. Hence, GPU-mode is
+recommended.
+
+:::
+
+## Ask (Sync)
+
+Passing the link of this image the following code:
+
+
+
+
+
+You will get a response similar to:
+
+:::tip[LLM Response]
+This image features a white boat with brown cushions, where a dog is sitting on the back of the boat. The dog seems to
+be enjoying its time outdoors, perhaps on a lake.
+:::
\ No newline at end of file
diff --git a/docs/docs/apis-generate/generate-with-tools.md b/docs/docs/apis-generate/generate-with-tools.md
index d25a5fc..291ccd5 100644
--- a/docs/docs/apis-generate/generate-with-tools.md
+++ b/docs/docs/apis-generate/generate-with-tools.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 6
+sidebar_position: 5
---
import CodeEmbed from '@site/src/components/CodeEmbed';
@@ -79,7 +79,7 @@ Now put it all together by registering the tools and prompting with tools.
Run this full example and you will get a response similar to:
-::::tip[LLM Response]
+:::tip[LLM Response]
[Result of executing tool 'current-fuel-price']: Current price of petrol in Bengaluru is Rs.103/L
@@ -88,4 +88,4 @@ Run this full example and you will get a response similar to:
[Result of executing tool 'get-employee-details']: Employee Details `{ID: 6bad82e6-b1a1-458f-a139-e3b646e092b1, Name:
Rahul Kumar, Address: King St, Hyderabad, India, Phone: 9876543210}`
-::::
+:::
diff --git a/docs/docs/apis-generate/generate.md b/docs/docs/apis-generate/generate.md
index 553a014..0eb9b05 100644
--- a/docs/docs/apis-generate/generate.md
+++ b/docs/docs/apis-generate/generate.md
@@ -1,11 +1,11 @@
---
-sidebar_position: 1
+sidebar_position: 2
---
import CodeEmbed from '@site/src/components/CodeEmbed';
import TypewriterTextarea from '@site/src/components/TypewriterTextarea';
-# Generate (Sync)
+# Generate
This API lets you ask questions to the LLMs in a synchronous way.
This API corresponds to
@@ -22,10 +22,10 @@ to [this](/apis-extras/options-builder).
You will get a response similar to:
-::::tip[LLM Response]
+:::tip[LLM Response]
I am a model of an AI trained by Mistral AI. I was designed to assist with a wide range of tasks, from answering
questions to helping with complex computations and research. How can I help you toda
-::::
+:::
### Try asking a question, receiving the answer streamed
@@ -49,7 +49,7 @@ width='100%'
You will get a response similar to:
-::::tip[LLM Response]
+:::tip[LLM Response]
```json
{
@@ -58,12 +58,12 @@ You will get a response similar to:
}
```
-::::
+:::
### With response mapped to specified class type
-::::tip[LLM Response]
+:::tip[LLM Response]
HeroInfo(heroName=Batman, ageOfPerson=30)
-::::
\ No newline at end of file
+:::
\ No newline at end of file
diff --git a/docs/docs/apis-model-management/_category_.json b/docs/docs/apis-model-management/_category_.json
index 53539cf..48f345c 100644
--- a/docs/docs/apis-model-management/_category_.json
+++ b/docs/docs/apis-model-management/_category_.json
@@ -1,5 +1,5 @@
{
- "label": "APIs - Model Management",
+ "label": "APIs - Manage Models",
"position": 2,
"link": {
"type": "generated-index",
diff --git a/metrics.png b/metrics.png
new file mode 100644
index 0000000..cc81197
Binary files /dev/null and b/metrics.png differ
diff --git a/pom.xml b/pom.xml
index 60cb5b1..3132f56 100644
--- a/pom.xml
+++ b/pom.xml
@@ -163,6 +163,69 @@
Etc/UTC
+
+
+ com.diffplug.spotless
+ spotless-maven-plugin
+ 2.46.1
+
+
+
+
+
+
+ .gitattributes
+ .gitignore
+
+
+
+
+
+ true
+ 4
+
+
+
+
+
+
+
+
+
+ 1.28.0
+
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+ check
+
+ compile
+
+
+
@@ -212,6 +275,7 @@
slf4j-api2.0.17
+
org.junit.jupiterjunit-jupiter-api
@@ -243,6 +307,19 @@
1.21.3test
+
+
+
+ io.prometheus
+ simpleclient
+ 0.16.0
+
+
+
+ com.google.guava
+ guava
+ 33.5.0-jre
+
diff --git a/src/main/java/io/github/ollama4j/OllamaAPI.java b/src/main/java/io/github/ollama4j/OllamaAPI.java
index a399adf..7e095d2 100644
--- a/src/main/java/io/github/ollama4j/OllamaAPI.java
+++ b/src/main/java/io/github/ollama4j/OllamaAPI.java
@@ -1,20 +1,26 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j;
-import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.ObjectMapper;
-import io.github.ollama4j.exceptions.OllamaBaseException;
+import io.github.ollama4j.exceptions.OllamaException;
import io.github.ollama4j.exceptions.RoleNotFoundException;
import io.github.ollama4j.exceptions.ToolInvocationException;
-import io.github.ollama4j.exceptions.ToolNotFoundException;
+import io.github.ollama4j.metrics.MetricsRecorder;
import io.github.ollama4j.models.chat.*;
-import io.github.ollama4j.models.embeddings.OllamaEmbedRequestModel;
-import io.github.ollama4j.models.embeddings.OllamaEmbedResponseModel;
-import io.github.ollama4j.models.embeddings.OllamaEmbeddingResponseModel;
-import io.github.ollama4j.models.embeddings.OllamaEmbeddingsRequestModel;
+import io.github.ollama4j.models.chat.OllamaChatTokenHandler;
+import io.github.ollama4j.models.embed.OllamaEmbedRequest;
+import io.github.ollama4j.models.embed.OllamaEmbedResult;
import io.github.ollama4j.models.generate.OllamaGenerateRequest;
-import io.github.ollama4j.models.generate.OllamaStreamHandler;
-import io.github.ollama4j.models.generate.OllamaTokenHandler;
-import io.github.ollama4j.models.ps.ModelsProcessResponse;
+import io.github.ollama4j.models.generate.OllamaGenerateStreamObserver;
+import io.github.ollama4j.models.generate.OllamaGenerateTokenHandler;
+import io.github.ollama4j.models.ps.ModelProcessesResult;
import io.github.ollama4j.models.request.*;
import io.github.ollama4j.models.response.*;
import io.github.ollama4j.tools.*;
@@ -22,16 +28,7 @@ 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.Options;
import io.github.ollama4j.utils.Utils;
-import lombok.Setter;
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Document;
-import org.jsoup.nodes.Element;
-import org.jsoup.select.Elements;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -39,7 +36,6 @@ import java.lang.reflect.Parameter;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
-import java.net.http.HttpConnectTimeoutException;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
@@ -47,45 +43,52 @@ import java.nio.file.Files;
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;
+import lombok.Setter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
- * The base Ollama API class.
+ * The main API class for interacting with the Ollama server.
+ *
+ *
This class provides methods for model management, chat, embeddings, tool registration, and more.
*/
-@SuppressWarnings({"DuplicatedCode", "resource"})
+@SuppressWarnings({"DuplicatedCode", "resource", "SpellCheckingInspection"})
public class OllamaAPI {
private static final Logger LOG = LoggerFactory.getLogger(OllamaAPI.class);
private final String host;
private Auth auth;
+
private final ToolRegistry toolRegistry = new ToolRegistry();
/**
* 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;
+ @Setter private long requestTimeoutSeconds = 10;
+
+ /** The read timeout in seconds for image URLs. */
+ @Setter private int imageURLReadTimeoutSeconds = 10;
+
+ /** The connect timeout in seconds for image URLs. */
+ @Setter private int imageURLConnectTimeoutSeconds = 10;
/**
* 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 failure.
- * Default is 3.
+ * 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;
+ @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 the model
- * up to the specified number of times in case of failure.
+ * 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).
*/
@@ -94,26 +97,24 @@ public class OllamaAPI {
private int numberOfRetriesForModelPull = 0;
/**
- * When set to true, tools will not be automatically executed by the library.
- * Instead, tool calls will be returned to the client for manual handling.
+ * Enable or disable Prometheus metrics collection.
*
- * Default is false for backward compatibility.
+ * 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 clientHandlesTools = false;
+ @Setter private boolean metricsEnabled = false;
/**
- * Instantiates the Ollama API with default Ollama host:
- * http://localhost:11434
- **/
+ * Instantiates the Ollama API with the default Ollama host: {@code http://localhost:11434}
+ */
public OllamaAPI() {
this.host = "http://localhost:11434";
}
/**
- * Instantiates the Ollama API with specified Ollama host address.
+ * Instantiates the Ollama API with a specified Ollama host address.
*
- * @param host the host address of Ollama server
+ * @param host the host address of the Ollama server
*/
public OllamaAPI(String host) {
if (host.endsWith("/")) {
@@ -121,12 +122,11 @@ public class OllamaAPI {
} else {
this.host = host;
}
- LOG.info("Ollama API initialized with host: {}", this.host);
+ LOG.info("Ollama4j client initialized. Connected to Ollama server at: {}", this.host);
}
/**
- * Set basic authentication for accessing 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
@@ -136,8 +136,7 @@ public class OllamaAPI {
}
/**
- * Set Bearer authentication for accessing 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
*/
@@ -146,64 +145,90 @@ public class OllamaAPI {
}
/**
- * API to check the reachability of Ollama server.
+ * Checks the reachability of the Ollama server.
*
- * @return true if the server is reachable, false otherwise.
+ * @return true if the server is reachable, false otherwise
+ * @throws OllamaException if the ping fails
*/
- public boolean ping() {
- String url = this.host + "/api/tags";
- HttpClient httpClient = HttpClient.newHttpClient();
- HttpRequest httpRequest;
- try {
- httpRequest = getRequestBuilderDefault(new URI(url))
- .header(Constants.HttpConstants.HEADER_KEY_ACCEPT, Constants.HttpConstants.APPLICATION_JSON)
- .header(Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE, Constants.HttpConstants.APPLICATION_JSON)
- .GET()
- .build();
- } catch (URISyntaxException e) {
- throw new RuntimeException(e);
- }
- HttpResponse response;
+ public boolean ping() throws OllamaException {
+ long startTime = System.currentTimeMillis();
+ String url = "/api/tags";
+ int statusCode = -1;
+ Object out = null;
try {
+ HttpClient httpClient = HttpClient.newHttpClient();
+ HttpRequest httpRequest;
+ HttpResponse response;
+ httpRequest =
+ getRequestBuilderDefault(new URI(this.host + url))
+ .header(
+ Constants.HttpConstants.HEADER_KEY_ACCEPT,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .header(
+ Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .GET()
+ .build();
response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
- } catch (HttpConnectTimeoutException e) {
- return false;
- } catch (IOException | InterruptedException e) {
- throw new RuntimeException(e);
+ statusCode = response.statusCode();
+ return statusCode == 200;
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ throw new OllamaException("Ping interrupted", ie);
+ } catch (Exception e) {
+ throw new OllamaException("Ping failed", e);
+ } finally {
+ MetricsRecorder.record(
+ url, "", false, false, false, null, null, startTime, statusCode, out);
}
- int statusCode = response.statusCode();
- return statusCode == 200;
}
/**
- * Provides a list of running models and details about each model currently
- * loaded into memory.
+ * Provides a list of running models and details about each model currently loaded into memory.
*
- * @return ModelsProcessResponse containing details about the running models
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- * @throws OllamaBaseException if the response indicates an error status
+ * @return ModelsProcessResult containing details about the running models
+ * @throws OllamaException if the response indicates an error status
*/
- public ModelsProcessResponse ps() throws IOException, InterruptedException, OllamaBaseException {
- String url = this.host + "/api/ps";
- HttpClient httpClient = HttpClient.newHttpClient();
- HttpRequest httpRequest = null;
+ public ModelProcessesResult ps() throws OllamaException {
+ long startTime = System.currentTimeMillis();
+ String url = "/api/ps";
+ int statusCode = -1;
+ Object out = null;
try {
- httpRequest = getRequestBuilderDefault(new URI(url))
- .header(Constants.HttpConstants.HEADER_KEY_ACCEPT, Constants.HttpConstants.APPLICATION_JSON)
- .header(Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE, Constants.HttpConstants.APPLICATION_JSON)
- .GET().build();
- } catch (URISyntaxException e) {
- throw new RuntimeException(e);
- }
- HttpResponse response = null;
- response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
- int statusCode = response.statusCode();
- String responseString = response.body();
- if (statusCode == 200) {
- return Utils.getObjectMapper().readValue(responseString, ModelsProcessResponse.class);
- } else {
- throw new OllamaBaseException(statusCode + " - " + responseString);
+ HttpClient httpClient = HttpClient.newHttpClient();
+ HttpRequest httpRequest = null;
+ try {
+ httpRequest =
+ getRequestBuilderDefault(new URI(this.host + url))
+ .header(
+ Constants.HttpConstants.HEADER_KEY_ACCEPT,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .header(
+ Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .GET()
+ .build();
+ } catch (URISyntaxException e) {
+ throw new OllamaException(e.getMessage(), e);
+ }
+ HttpResponse response = null;
+ response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
+ statusCode = response.statusCode();
+ String responseString = response.body();
+ if (statusCode == 200) {
+ return Utils.getObjectMapper()
+ .readValue(responseString, ModelProcessesResult.class);
+ } else {
+ throw new OllamaException(statusCode + " - " + responseString);
+ }
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ throw new OllamaException("ps interrupted", ie);
+ } catch (Exception e) {
+ throw new OllamaException("ps failed", e);
+ } finally {
+ MetricsRecorder.record(
+ url, "", false, false, false, null, null, startTime, statusCode, out);
}
}
@@ -211,256 +236,68 @@ public class OllamaAPI {
* Lists available models from the Ollama server.
*
* @return a list of models available on the server
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- * @throws URISyntaxException if the URI for the request is malformed
+ * @throws OllamaException if the response indicates an error status
*/
- public List listModels() throws OllamaBaseException, IOException, InterruptedException, URISyntaxException {
- String url = this.host + "/api/tags";
- HttpClient httpClient = HttpClient.newHttpClient();
- HttpRequest httpRequest = getRequestBuilderDefault(new URI(url))
- .header(Constants.HttpConstants.HEADER_KEY_ACCEPT, Constants.HttpConstants.APPLICATION_JSON)
- .header(Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE, Constants.HttpConstants.APPLICATION_JSON).GET()
- .build();
- HttpResponse response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
- int statusCode = response.statusCode();
- String responseString = response.body();
- if (statusCode == 200) {
- return Utils.getObjectMapper().readValue(responseString, ListModelsResponse.class).getModels();
- } else {
- throw new OllamaBaseException(statusCode + " - " + responseString);
- }
- }
-
- /**
- * Retrieves a list of models from the Ollama library. This method fetches the
- * available models directly from Ollama
- * library page, including model details such as the name, pull count, popular
- * tags, tag count, and the time when model was updated.
- *
- * @return A list of {@link LibraryModel} objects representing the models
- * available in the Ollama library.
- * @throws OllamaBaseException If the HTTP request fails or the response is not
- * successful (non-200 status code).
- * @throws IOException If an I/O error occurs during the HTTP request
- * or response processing.
- * @throws InterruptedException If the thread executing the request is
- * interrupted.
- * @throws URISyntaxException If there is an error creating the URI for the
- * HTTP request.
- */
- public List listModelsFromLibrary()
- throws OllamaBaseException, IOException, InterruptedException, URISyntaxException {
- String url = "https://ollama.com/library";
- HttpClient httpClient = HttpClient.newHttpClient();
- HttpRequest httpRequest = getRequestBuilderDefault(new URI(url))
- .header(Constants.HttpConstants.HEADER_KEY_ACCEPT, Constants.HttpConstants.APPLICATION_JSON)
- .header(Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE, Constants.HttpConstants.APPLICATION_JSON).GET()
- .build();
- HttpResponse response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
- int statusCode = response.statusCode();
- String responseString = response.body();
- List models = new ArrayList<>();
- if (statusCode == 200) {
- Document doc = Jsoup.parse(responseString);
- Elements modelSections = doc.selectXpath("//*[@id='repo']/ul/li/a");
- for (Element e : modelSections) {
- LibraryModel model = new LibraryModel();
- Elements names = e.select("div > h2 > div > span");
- Elements desc = e.select("div > p");
- Elements pullCounts = e.select("div:nth-of-type(2) > p > span:first-of-type > span:first-of-type");
- Elements popularTags = e.select("div > div > span");
- Elements totalTags = e.select("div:nth-of-type(2) > p > span:nth-of-type(2) > span:first-of-type");
- Elements lastUpdatedTime = e
- .select("div:nth-of-type(2) > p > span:nth-of-type(3) > span:nth-of-type(2)");
-
- if (names.first() == null || names.isEmpty()) {
- // if name cannot be extracted, skip.
- continue;
- }
- Optional.ofNullable(names.first()).map(Element::text).ifPresent(model::setName);
- model.setDescription(Optional.ofNullable(desc.first()).map(Element::text).orElse(""));
- model.setPopularTags(Optional.of(popularTags)
- .map(tags -> tags.stream().map(Element::text).collect(Collectors.toList()))
- .orElse(new ArrayList<>()));
- model.setPullCount(Optional.ofNullable(pullCounts.first()).map(Element::text).orElse(""));
- model.setTotalTags(
- Optional.ofNullable(totalTags.first()).map(Element::text).map(Integer::parseInt).orElse(0));
- model.setLastUpdated(Optional.ofNullable(lastUpdatedTime.first()).map(Element::text).orElse(""));
-
- models.add(model);
+ public List listModels() throws OllamaException {
+ long startTime = System.currentTimeMillis();
+ String url = "/api/tags";
+ int statusCode = -1;
+ Object out = null;
+ try {
+ HttpClient httpClient = HttpClient.newHttpClient();
+ HttpRequest httpRequest =
+ getRequestBuilderDefault(new URI(this.host + url))
+ .header(
+ Constants.HttpConstants.HEADER_KEY_ACCEPT,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .header(
+ Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .GET()
+ .build();
+ HttpResponse response =
+ httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
+ statusCode = response.statusCode();
+ String responseString = response.body();
+ if (statusCode == 200) {
+ return Utils.getObjectMapper()
+ .readValue(responseString, ListModelsResponse.class)
+ .getModels();
+ } else {
+ throw new OllamaException(statusCode + " - " + responseString);
}
- return models;
- } else {
- throw new OllamaBaseException(statusCode + " - " + responseString);
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ throw new OllamaException("listModels interrupted", ie);
+ } catch (Exception e) {
+ throw new OllamaException(e.getMessage(), e);
+ } finally {
+ MetricsRecorder.record(
+ url, "", false, false, false, null, null, startTime, statusCode, out);
}
}
- /**
- * Fetches the tags associated with a specific model from Ollama library.
- * This method fetches the available model tags directly from Ollama library
- * model page, including model tag name, size and time when model was last
- * updated
- * into a list of {@link LibraryModelTag} objects.
- *
- * @param libraryModel the {@link LibraryModel} object which contains the name
- * of the library model
- * for which the tags need to be fetched.
- * @return a list of {@link LibraryModelTag} objects containing the extracted
- * tags and their associated metadata.
- * @throws OllamaBaseException if the HTTP response status code indicates an
- * error (i.e., not 200 OK),
- * or if there is any other issue during the
- * request or response processing.
- * @throws IOException if an input/output exception occurs during the
- * HTTP request or response handling.
- * @throws InterruptedException if the thread is interrupted while waiting for
- * the HTTP response.
- * @throws URISyntaxException if the URI format is incorrect or invalid.
- */
- public LibraryModelDetail getLibraryModelDetails(LibraryModel libraryModel)
- throws OllamaBaseException, IOException, InterruptedException, URISyntaxException {
- String url = String.format("https://ollama.com/library/%s/tags", libraryModel.getName());
- HttpClient httpClient = HttpClient.newHttpClient();
- HttpRequest httpRequest = getRequestBuilderDefault(new URI(url))
- .header(Constants.HttpConstants.HEADER_KEY_ACCEPT, Constants.HttpConstants.APPLICATION_JSON)
- .header(Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE, Constants.HttpConstants.APPLICATION_JSON).GET()
- .build();
- HttpResponse response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
- int statusCode = response.statusCode();
- String responseString = response.body();
-
- List libraryModelTags = new ArrayList<>();
- if (statusCode == 200) {
- Document doc = Jsoup.parse(responseString);
- Elements tagSections = doc
- .select("html > body > main > div > section > div > div > div:nth-child(n+2) > div");
- for (Element e : tagSections) {
- Elements tags = e.select("div > a > div");
- Elements tagsMetas = e.select("div > span");
-
- LibraryModelTag libraryModelTag = new LibraryModelTag();
-
- if (tags.first() == null || tags.isEmpty()) {
- // if tag cannot be extracted, skip.
- continue;
- }
- libraryModelTag.setName(libraryModel.getName());
- Optional.ofNullable(tags.first()).map(Element::text).ifPresent(libraryModelTag::setTag);
- libraryModelTag.setSize(Optional.ofNullable(tagsMetas.first()).map(element -> element.text().split("•"))
- .filter(parts -> parts.length > 1).map(parts -> parts[1].trim()).orElse(""));
- libraryModelTag
- .setLastUpdated(Optional.ofNullable(tagsMetas.first()).map(element -> element.text().split("•"))
- .filter(parts -> parts.length > 1).map(parts -> parts[2].trim()).orElse(""));
- libraryModelTags.add(libraryModelTag);
- }
- LibraryModelDetail libraryModelDetail = new LibraryModelDetail();
- libraryModelDetail.setModel(libraryModel);
- libraryModelDetail.setTags(libraryModelTags);
- return libraryModelDetail;
- } else {
- throw new OllamaBaseException(statusCode + " - " + responseString);
- }
- }
-
- /**
- * Finds a specific model using model name and tag from Ollama library.
- *
- * Deprecated: This method relies on the HTML structure of the Ollama
- * website,
- * which is subject to change at any time. As a result, it is difficult to keep
- * this API
- * method consistently updated and reliable. Therefore, this method is
- * deprecated and
- * may be removed in future releases.
- *
- * This method retrieves the model from the Ollama library by its name, then
- * fetches its tags.
- * It searches through the tags of the model to find one that matches the
- * specified tag name.
- * If the model or the tag is not found, it throws a
- * {@link NoSuchElementException}.
- *
- * @param modelName The name of the model to search for in the library.
- * @param tag The tag name to search for within the specified model.
- * @return The {@link LibraryModelTag} associated with the specified model and
- * tag.
- * @throws OllamaBaseException If there is a problem with the Ollama library
- * operations.
- * @throws IOException If an I/O error occurs during the operation.
- * @throws URISyntaxException If there is an error with the URI syntax.
- * @throws InterruptedException If the operation is interrupted.
- * @throws NoSuchElementException If the model or the tag is not found.
- * @deprecated This method relies on the HTML structure of the Ollama website,
- * which can change at any time and break this API. It is deprecated
- * and may be removed in the future.
- */
- @Deprecated
- public LibraryModelTag findModelTagFromLibrary(String modelName, String tag)
- throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
- List libraryModels = this.listModelsFromLibrary();
- LibraryModel libraryModel = libraryModels.stream().filter(model -> model.getName().equals(modelName))
- .findFirst().orElseThrow(
- () -> new NoSuchElementException(String.format("Model by name '%s' not found", modelName)));
- LibraryModelDetail libraryModelDetail = this.getLibraryModelDetails(libraryModel);
- return libraryModelDetail.getTags().stream().filter(tagName -> tagName.getTag().equals(tag)).findFirst()
- .orElseThrow(() -> new NoSuchElementException(
- String.format("Tag '%s' for model '%s' not found", tag, modelName)));
- }
-
- /**
- * Pull a model on the Ollama server from the list of available models.
- *
- * If {@code numberOfRetriesForModelPull} is greater than 0, this method will
- * retry pulling the model
- * up to the specified number of times if an {@link OllamaBaseException} occurs,
- * using exponential backoff
- * between retries (delay doubles after each failed attempt, starting at 1
- * second).
- *
- * The backoff is only applied between retries, not after the final attempt.
- *
- * @param modelName the name of the model
- * @throws OllamaBaseException if the response indicates an error status or all
- * retries fail
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted or the thread is
- * interrupted during backoff
- * @throws URISyntaxException if the URI for the request is malformed
- */
- public void pullModel(String modelName)
- throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
- if (numberOfRetriesForModelPull == 0) {
- this.doPullModel(modelName);
- return;
- }
- int numberOfRetries = 0;
- long baseDelayMillis = 3000L; // 1 second base delay
- while (numberOfRetries < numberOfRetriesForModelPull) {
- try {
- this.doPullModel(modelName);
- return;
- } catch (OllamaBaseException e) {
- handlePullRetry(modelName, numberOfRetries, numberOfRetriesForModelPull, baseDelayMillis);
- numberOfRetries++;
- }
- }
- throw new OllamaBaseException(
- "Failed to pull model " + modelName + " after " + numberOfRetriesForModelPull + " retries");
- }
-
/**
* Handles retry backoff for pullModel.
+ *
+ * @param modelName the name of the model being pulled
+ * @param currentRetry the current retry attempt (zero-based)
+ * @param maxRetries the maximum number of retries allowed
+ * @param baseDelayMillis the base delay in milliseconds for exponential backoff
+ * @throws InterruptedException if the thread is interrupted during sleep
*/
- private void handlePullRetry(String modelName, int currentRetry, int maxRetries, long baseDelayMillis)
+ private void handlePullRetry(
+ String modelName, int currentRetry, int maxRetries, long baseDelayMillis)
throws InterruptedException {
int attempt = currentRetry + 1;
if (attempt < maxRetries) {
long backoffMillis = baseDelayMillis * (1L << currentRetry);
- LOG.error("Failed to pull model {}, retrying in {}s... (attempt {}/{})",
- modelName, backoffMillis / 1000, attempt, maxRetries);
+ LOG.error(
+ "Failed to pull model {}, retrying in {}s... (attempt {}/{})",
+ modelName,
+ backoffMillis / 1000,
+ attempt,
+ maxRetries);
try {
Thread.sleep(backoffMillis);
} catch (InterruptedException ie) {
@@ -468,963 +305,668 @@ public class OllamaAPI {
throw ie;
}
} else {
- LOG.error("Failed to pull model {} after {} attempts, no more retries.", modelName, maxRetries);
+ LOG.error(
+ "Failed to pull model {} after {} attempts, no more retries.",
+ modelName,
+ maxRetries);
}
}
- private void doPullModel(String modelName)
- throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
- String url = this.host + "/api/pull";
- String jsonData = new ModelRequest(modelName).toString();
- HttpRequest request = getRequestBuilderDefault(new URI(url)).POST(HttpRequest.BodyPublishers.ofString(jsonData))
- .header(Constants.HttpConstants.HEADER_KEY_ACCEPT, Constants.HttpConstants.APPLICATION_JSON)
- .header(Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE, Constants.HttpConstants.APPLICATION_JSON)
- .build();
- HttpClient client = HttpClient.newHttpClient();
- HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
- int statusCode = response.statusCode();
- InputStream responseBodyStream = response.body();
- String responseString = "";
- boolean success = false; // Flag to check the pull success.
- try (BufferedReader reader = new BufferedReader(
- new InputStreamReader(responseBodyStream, StandardCharsets.UTF_8))) {
- String line;
- while ((line = reader.readLine()) != null) {
- ModelPullResponse modelPullResponse = Utils.getObjectMapper().readValue(line, ModelPullResponse.class);
- if (modelPullResponse != null) {
- // Check for error in response body first
- if (modelPullResponse.getError() != null && !modelPullResponse.getError().trim().isEmpty()) {
- throw new OllamaBaseException("Model pull failed: " + modelPullResponse.getError());
- }
+ /**
+ * Internal method to pull a model from the Ollama server.
+ *
+ * @param modelName the name of the model to pull
+ * @throws OllamaException if the pull fails
+ */
+ private void doPullModel(String modelName) throws OllamaException {
+ long startTime = System.currentTimeMillis();
+ String url = "/api/pull";
+ int statusCode = -1;
+ Object out = null;
+ try {
+ String jsonData = new ModelRequest(modelName).toString();
+ HttpRequest request =
+ getRequestBuilderDefault(new URI(this.host + url))
+ .POST(HttpRequest.BodyPublishers.ofString(jsonData))
+ .header(
+ Constants.HttpConstants.HEADER_KEY_ACCEPT,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .header(
+ Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .build();
+ HttpClient client = HttpClient.newHttpClient();
+ HttpResponse response =
+ client.send(request, HttpResponse.BodyHandlers.ofInputStream());
+ statusCode = response.statusCode();
+ InputStream responseBodyStream = response.body();
+ String responseString = "";
+ boolean success = false; // Flag to check the pull success.
- if (modelPullResponse.getStatus() != null) {
- LOG.info("{}: {}", modelName, modelPullResponse.getStatus());
- // Check if status is "success" and set success flag to true.
- if ("success".equalsIgnoreCase(modelPullResponse.getStatus())) {
- success = true;
- }
- }
- } else {
- LOG.error("Received null response for model pull.");
+ try (BufferedReader reader =
+ new BufferedReader(
+ new InputStreamReader(responseBodyStream, StandardCharsets.UTF_8))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ ModelPullResponse modelPullResponse =
+ Utils.getObjectMapper().readValue(line, ModelPullResponse.class);
+ success = processModelPullResponse(modelPullResponse, modelName) || success;
}
}
- }
- if (!success) {
- LOG.error("Model pull failed or returned invalid status.");
- throw new OllamaBaseException("Model pull failed or returned invalid status.");
- }
- if (statusCode != 200) {
- throw new OllamaBaseException(statusCode + " - " + responseString);
+ if (!success) {
+ LOG.error("Model pull failed or returned invalid status.");
+ throw new OllamaException("Model pull failed or returned invalid status.");
+ }
+ if (statusCode != 200) {
+ throw new OllamaException(statusCode + " - " + responseString);
+ }
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ throw new OllamaException("Thread was interrupted during model pull.", ie);
+ } catch (Exception e) {
+ throw new OllamaException(e.getMessage(), e);
+ } finally {
+ MetricsRecorder.record(
+ url, "", false, false, false, null, null, startTime, statusCode, out);
}
}
- public String getVersion() throws URISyntaxException, IOException, InterruptedException, OllamaBaseException {
- String url = this.host + "/api/version";
- HttpClient httpClient = HttpClient.newHttpClient();
- HttpRequest httpRequest = getRequestBuilderDefault(new URI(url))
- .header(Constants.HttpConstants.HEADER_KEY_ACCEPT, Constants.HttpConstants.APPLICATION_JSON)
- .header(Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE, Constants.HttpConstants.APPLICATION_JSON).GET()
- .build();
- HttpResponse response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
- int statusCode = response.statusCode();
- String responseString = response.body();
- if (statusCode == 200) {
- return Utils.getObjectMapper().readValue(responseString, OllamaVersion.class).getVersion();
- } else {
- throw new OllamaBaseException(statusCode + " - " + responseString);
+ /**
+ * 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
+ * @return true if the pull was successful, false otherwise
+ * @throws OllamaException if the response contains an error
+ */
+ @SuppressWarnings("RedundantIfStatement")
+ private boolean processModelPullResponse(ModelPullResponse modelPullResponse, String modelName)
+ throws OllamaException {
+ if (modelPullResponse == null) {
+ LOG.error("Received null response for model pull.");
+ return false;
+ }
+ String error = modelPullResponse.getError();
+ if (error != null && !error.trim().isEmpty()) {
+ throw new OllamaException("Model pull failed: " + error);
+ }
+ String status = modelPullResponse.getStatus();
+ if (status != null) {
+ LOG.debug("{}: {}", modelName, status);
+ if ("success".equalsIgnoreCase(status)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets the Ollama server version.
+ *
+ * @return the version string
+ * @throws OllamaException if the request fails
+ */
+ public String getVersion() throws OllamaException {
+ String url = "/api/version";
+ long startTime = System.currentTimeMillis();
+ int statusCode = -1;
+ Object out = null;
+ try {
+ HttpClient httpClient = HttpClient.newHttpClient();
+ HttpRequest httpRequest =
+ getRequestBuilderDefault(new URI(this.host + url))
+ .header(
+ Constants.HttpConstants.HEADER_KEY_ACCEPT,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .header(
+ Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .GET()
+ .build();
+ HttpResponse response =
+ httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
+ statusCode = response.statusCode();
+ String responseString = response.body();
+ if (statusCode == 200) {
+ return Utils.getObjectMapper()
+ .readValue(responseString, OllamaVersion.class)
+ .getVersion();
+ } else {
+ throw new OllamaException(statusCode + " - " + responseString);
+ }
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ throw new OllamaException("Thread was interrupted", ie);
+ } catch (Exception e) {
+ throw new OllamaException(e.getMessage(), e);
+ } finally {
+ MetricsRecorder.record(
+ url, "", false, false, false, null, null, startTime, statusCode, out);
}
}
/**
* 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
+ * 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 libraryModelTag the {@link LibraryModelTag} object containing the name
- * and tag
- * of the model to be pulled.
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- * @throws URISyntaxException if the URI for the request is malformed
+ * @param modelName the name/tag of the model to be pulled. Ex: llama3:latest
+ * @throws OllamaException if the response indicates an error status
*/
- public void pullModel(LibraryModelTag libraryModelTag)
- throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
- String tagToPull = String.format("%s:%s", libraryModelTag.getName(), libraryModelTag.getTag());
- pullModel(tagToPull);
+ public void pullModel(String modelName) throws OllamaException {
+ try {
+ if (numberOfRetriesForModelPull == 0) {
+ this.doPullModel(modelName);
+ return;
+ }
+ int numberOfRetries = 0;
+ long baseDelayMillis = 3000L; // 3 seconds base delay
+ while (numberOfRetries < numberOfRetriesForModelPull) {
+ try {
+ this.doPullModel(modelName);
+ return;
+ } catch (OllamaException e) {
+ handlePullRetry(
+ modelName,
+ numberOfRetries,
+ numberOfRetriesForModelPull,
+ baseDelayMillis);
+ numberOfRetries++;
+ }
+ }
+ throw new OllamaException(
+ "Failed to pull model "
+ + modelName
+ + " after "
+ + numberOfRetriesForModelPull
+ + " retries");
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ throw new OllamaException("Thread was interrupted", ie);
+ } catch (Exception e) {
+ throw new OllamaException(e.getMessage(), e);
+ }
}
/**
* Gets model details from the Ollama server.
*
- * @param modelName the model
+ * @param modelName the model name
* @return the model details
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- * @throws URISyntaxException if the URI for the request is malformed
+ * @throws OllamaException if the response indicates an error status
*/
- public ModelDetail getModelDetails(String modelName)
- throws IOException, OllamaBaseException, InterruptedException, URISyntaxException {
- String url = this.host + "/api/show";
- String jsonData = new ModelRequest(modelName).toString();
- HttpRequest request = getRequestBuilderDefault(new URI(url))
- .header(Constants.HttpConstants.HEADER_KEY_ACCEPT, Constants.HttpConstants.APPLICATION_JSON)
- .header(Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE, Constants.HttpConstants.APPLICATION_JSON)
- .POST(HttpRequest.BodyPublishers.ofString(jsonData)).build();
- HttpClient client = HttpClient.newHttpClient();
- HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
- int statusCode = response.statusCode();
- String responseBody = response.body();
- if (statusCode == 200) {
- return Utils.getObjectMapper().readValue(responseBody, ModelDetail.class);
- } else {
- throw new OllamaBaseException(statusCode + " - " + responseBody);
+ public ModelDetail getModelDetails(String modelName) throws OllamaException {
+ long startTime = System.currentTimeMillis();
+ String url = "/api/show";
+ int statusCode = -1;
+ Object out = null;
+ try {
+ String jsonData = new ModelRequest(modelName).toString();
+ HttpRequest request =
+ getRequestBuilderDefault(new URI(this.host + url))
+ .header(
+ Constants.HttpConstants.HEADER_KEY_ACCEPT,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .header(
+ Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .POST(HttpRequest.BodyPublishers.ofString(jsonData))
+ .build();
+ HttpClient client = HttpClient.newHttpClient();
+ HttpResponse response =
+ client.send(request, HttpResponse.BodyHandlers.ofString());
+ statusCode = response.statusCode();
+ String responseBody = response.body();
+ if (statusCode == 200) {
+ return Utils.getObjectMapper().readValue(responseBody, ModelDetail.class);
+ } else {
+ throw new OllamaException(statusCode + " - " + responseBody);
+ }
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ throw new OllamaException("Thread was interrupted", ie);
+ } catch (Exception e) {
+ throw new OllamaException(e.getMessage(), e);
+ } finally {
+ MetricsRecorder.record(
+ url, "", false, false, false, null, null, startTime, statusCode, out);
}
}
/**
- * Create a custom model from a model file. Read more about custom model file
- * creation here.
- *
- * @param modelName the name of the custom model to be created.
- * @param modelFilePath the path to model file that exists on the Ollama server.
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- * @throws URISyntaxException if the URI for the request is malformed
- */
- @Deprecated
- public void createModelWithFilePath(String modelName, String modelFilePath)
- throws IOException, InterruptedException, OllamaBaseException, URISyntaxException {
- String url = this.host + "/api/create";
- String jsonData = new CustomModelFilePathRequest(modelName, modelFilePath).toString();
- HttpRequest request = getRequestBuilderDefault(new URI(url))
- .header(Constants.HttpConstants.HEADER_KEY_ACCEPT, Constants.HttpConstants.APPLICATION_JSON)
- .header(Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE, Constants.HttpConstants.APPLICATION_JSON)
- .POST(HttpRequest.BodyPublishers.ofString(jsonData, StandardCharsets.UTF_8)).build();
- HttpClient client = HttpClient.newHttpClient();
- HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
- int statusCode = response.statusCode();
- String responseString = response.body();
- if (statusCode != 200) {
- throw new OllamaBaseException(statusCode + " - " + responseString);
- }
- // FIXME: Ollama API returns HTTP status code 200 for model creation failure
- // cases. Correct this
- // if the issue is fixed in the Ollama API server.
- if (responseString.contains("error")) {
- throw new OllamaBaseException(responseString);
- }
- LOG.debug(responseString);
- }
-
- /**
- * Create a custom model from a model file. Read more about custom model file
- * creation here.
- *
- * @param modelName the name of the custom model to be created.
- * @param modelFileContents the path to model file that exists on the Ollama
- * server.
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- * @throws URISyntaxException if the URI for the request is malformed
- */
- @Deprecated
- public void createModelWithModelFileContents(String modelName, String modelFileContents)
- throws IOException, InterruptedException, OllamaBaseException, URISyntaxException {
- String url = this.host + "/api/create";
- String jsonData = new CustomModelFileContentsRequest(modelName, modelFileContents).toString();
- HttpRequest request = getRequestBuilderDefault(new URI(url))
- .header(Constants.HttpConstants.HEADER_KEY_ACCEPT, Constants.HttpConstants.APPLICATION_JSON)
- .header(Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE, Constants.HttpConstants.APPLICATION_JSON)
- .POST(HttpRequest.BodyPublishers.ofString(jsonData, StandardCharsets.UTF_8)).build();
- HttpClient client = HttpClient.newHttpClient();
- HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
- int statusCode = response.statusCode();
- String responseString = response.body();
- if (statusCode != 200) {
- throw new OllamaBaseException(statusCode + " - " + responseString);
- }
- if (responseString.contains("error")) {
- throw new OllamaBaseException(responseString);
- }
- LOG.debug(responseString);
- }
-
- /**
- * Create 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
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- * @throws URISyntaxException if the URI for the request is malformed
+ * @throws OllamaException if the response indicates an error status
*/
- public void createModel(CustomModelRequest customModelRequest)
- throws IOException, InterruptedException, OllamaBaseException, URISyntaxException {
- String url = this.host + "/api/create";
- String jsonData = customModelRequest.toString();
- HttpRequest request = getRequestBuilderDefault(new URI(url))
- .header(Constants.HttpConstants.HEADER_KEY_ACCEPT, Constants.HttpConstants.APPLICATION_JSON)
- .header(Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE, Constants.HttpConstants.APPLICATION_JSON)
- .POST(HttpRequest.BodyPublishers.ofString(jsonData, StandardCharsets.UTF_8)).build();
- HttpClient client = HttpClient.newHttpClient();
- HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
- int statusCode = response.statusCode();
- String responseString = response.body();
- if (statusCode != 200) {
- throw new OllamaBaseException(statusCode + " - " + responseString);
+ public void createModel(CustomModelRequest customModelRequest) throws OllamaException {
+ long startTime = System.currentTimeMillis();
+ String url = "/api/create";
+ int statusCode = -1;
+ Object out = null;
+ try {
+ String jsonData = customModelRequest.toString();
+ HttpRequest request =
+ getRequestBuilderDefault(new URI(this.host + url))
+ .header(
+ Constants.HttpConstants.HEADER_KEY_ACCEPT,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .header(
+ Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .POST(
+ HttpRequest.BodyPublishers.ofString(
+ jsonData, StandardCharsets.UTF_8))
+ .build();
+ HttpClient client = HttpClient.newHttpClient();
+ HttpResponse response =
+ client.send(request, HttpResponse.BodyHandlers.ofInputStream());
+ statusCode = response.statusCode();
+ if (statusCode != 200) {
+ String errorBody =
+ new String(response.body().readAllBytes(), StandardCharsets.UTF_8);
+ out = errorBody;
+ throw new OllamaException(statusCode + " - " + errorBody);
+ }
+ try (BufferedReader reader =
+ new BufferedReader(
+ new InputStreamReader(response.body(), StandardCharsets.UTF_8))) {
+ String line;
+ StringBuilder lines = new StringBuilder();
+ while ((line = reader.readLine()) != null) {
+ ModelPullResponse res =
+ Utils.getObjectMapper().readValue(line, ModelPullResponse.class);
+ lines.append(line);
+ LOG.debug(res.getStatus());
+ if (res.getError() != null) {
+ out = res.getError();
+ throw new OllamaException(res.getError());
+ }
+ }
+ out = lines;
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new OllamaException("Thread was interrupted", e);
+ } catch (Exception e) {
+ throw new OllamaException(e.getMessage(), e);
+ } finally {
+ MetricsRecorder.record(
+ url, "", false, false, false, null, null, startTime, statusCode, out);
}
- if (responseString.contains("error")) {
- throw new OllamaBaseException(responseString);
- }
- LOG.debug(responseString);
}
/**
- * Delete a model from Ollama server.
+ * 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 Ollama server.
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- * @throws URISyntaxException if the URI for the request is malformed
+ * @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
+ * @throws OllamaException if the response indicates an error status
*/
- public void deleteModel(String modelName, boolean ignoreIfNotPresent)
- throws IOException, InterruptedException, OllamaBaseException, URISyntaxException {
- String url = this.host + "/api/delete";
- String jsonData = new ModelRequest(modelName).toString();
- HttpRequest request = getRequestBuilderDefault(new URI(url))
- .method("DELETE", HttpRequest.BodyPublishers.ofString(jsonData, StandardCharsets.UTF_8))
- .header(Constants.HttpConstants.HEADER_KEY_ACCEPT, Constants.HttpConstants.APPLICATION_JSON)
- .header(Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE, Constants.HttpConstants.APPLICATION_JSON)
- .build();
- HttpClient client = HttpClient.newHttpClient();
- HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
- int statusCode = response.statusCode();
- String responseBody = response.body();
- if (statusCode == 404 && responseBody.contains("model") && responseBody.contains("not found")) {
- return;
- }
- if (statusCode != 200) {
- throw new OllamaBaseException(statusCode + " - " + responseBody);
+ public void deleteModel(String modelName, boolean ignoreIfNotPresent) throws OllamaException {
+ long startTime = System.currentTimeMillis();
+ String url = "/api/delete";
+ int statusCode = -1;
+ Object out = null;
+ try {
+ String jsonData = new ModelRequest(modelName).toString();
+ HttpRequest request =
+ getRequestBuilderDefault(new URI(this.host + url))
+ .method(
+ "DELETE",
+ HttpRequest.BodyPublishers.ofString(
+ jsonData, StandardCharsets.UTF_8))
+ .header(
+ Constants.HttpConstants.HEADER_KEY_ACCEPT,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .header(
+ Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .build();
+ HttpClient client = HttpClient.newHttpClient();
+ HttpResponse response =
+ client.send(request, HttpResponse.BodyHandlers.ofString());
+ statusCode = response.statusCode();
+ String responseBody = response.body();
+ out = responseBody;
+ if (statusCode == 404
+ && responseBody.contains("model")
+ && responseBody.contains("not found")) {
+ return;
+ }
+ if (statusCode != 200) {
+ throw new OllamaException(statusCode + " - " + responseBody);
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new OllamaException("Thread was interrupted", e);
+ } catch (Exception e) {
+ throw new OllamaException(statusCode + " - " + out, e);
+ } finally {
+ MetricsRecorder.record(
+ url, "", false, false, false, null, null, startTime, statusCode, out);
}
}
/**
- * Generate embeddings for a given text from a model
+ * Unloads a model from memory.
+ *
+ * If an empty prompt is provided and the keep_alive parameter is set to 0, a model will be
+ * unloaded from memory.
*
- * @param model name of model to generate embeddings from
- * @param prompt text to generate embeddings for
- * @return embeddings
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- * @deprecated Use {@link #embed(String, List)} instead.
+ * @param modelName the name of the model to unload
+ * @throws OllamaException if the response indicates an error status
*/
- @Deprecated
- public List generateEmbeddings(String model, String prompt)
- throws IOException, InterruptedException, OllamaBaseException {
- return generateEmbeddings(new OllamaEmbeddingsRequestModel(model, prompt));
- }
-
- /**
- * Generate embeddings using a {@link OllamaEmbeddingsRequestModel}.
- *
- * @param modelRequest request for '/api/embeddings' endpoint
- * @return embeddings
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- * @deprecated Use {@link #embed(OllamaEmbedRequestModel)} instead.
- */
- @Deprecated
- public List generateEmbeddings(OllamaEmbeddingsRequestModel modelRequest)
- throws IOException, InterruptedException, OllamaBaseException {
- URI uri = URI.create(this.host + "/api/embeddings");
- String jsonData = modelRequest.toString();
- HttpClient httpClient = HttpClient.newHttpClient();
- HttpRequest.Builder requestBuilder = getRequestBuilderDefault(uri)
- .header(Constants.HttpConstants.HEADER_KEY_ACCEPT, Constants.HttpConstants.APPLICATION_JSON)
- .POST(HttpRequest.BodyPublishers.ofString(jsonData));
- HttpRequest request = requestBuilder.build();
- HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
- int statusCode = response.statusCode();
- String responseBody = response.body();
- if (statusCode == 200) {
- OllamaEmbeddingResponseModel embeddingResponse = Utils.getObjectMapper().readValue(responseBody,
- OllamaEmbeddingResponseModel.class);
- return embeddingResponse.getEmbedding();
- } else {
- throw new OllamaBaseException(statusCode + " - " + responseBody);
+ public void unloadModel(String modelName) throws OllamaException {
+ long startTime = System.currentTimeMillis();
+ String url = "/api/generate";
+ int statusCode = -1;
+ Object out = null;
+ try {
+ ObjectMapper objectMapper = new ObjectMapper();
+ Map jsonMap = new java.util.HashMap<>();
+ jsonMap.put("model", modelName);
+ jsonMap.put("keep_alive", 0);
+ String jsonData = objectMapper.writeValueAsString(jsonMap);
+ HttpRequest request =
+ getRequestBuilderDefault(new URI(this.host + url))
+ .method(
+ "POST",
+ HttpRequest.BodyPublishers.ofString(
+ jsonData, StandardCharsets.UTF_8))
+ .header(
+ Constants.HttpConstants.HEADER_KEY_ACCEPT,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .header(
+ Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .build();
+ LOG.debug("Unloading model with request: {}", jsonData);
+ HttpClient client = HttpClient.newHttpClient();
+ HttpResponse response =
+ client.send(request, HttpResponse.BodyHandlers.ofString());
+ statusCode = response.statusCode();
+ String responseBody = response.body();
+ if (statusCode == 404
+ && responseBody.contains("model")
+ && responseBody.contains("not found")) {
+ LOG.debug("Unload response: {} - {}", statusCode, responseBody);
+ return;
+ }
+ if (statusCode != 200) {
+ LOG.debug("Unload response: {} - {}", statusCode, responseBody);
+ throw new OllamaException(statusCode + " - " + responseBody);
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ LOG.debug("Unload interrupted: {} - {}", statusCode, out);
+ throw new OllamaException(statusCode + " - " + out, e);
+ } catch (Exception e) {
+ LOG.debug("Unload failed: {} - {}", statusCode, out);
+ throw new OllamaException(statusCode + " - " + out, e);
+ } finally {
+ MetricsRecorder.record(
+ url, "", false, false, false, null, null, startTime, statusCode, out);
}
}
/**
- * Generate embeddings for a given text from a model
- *
- * @param model name of model to generate embeddings from
- * @param inputs text/s to generate embeddings for
- * @return embeddings
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- */
- public OllamaEmbedResponseModel embed(String model, List inputs)
- throws IOException, InterruptedException, OllamaBaseException {
- return embed(new OllamaEmbedRequestModel(model, inputs));
- }
-
- /**
- * Generate embeddings using a {@link OllamaEmbedRequestModel}.
+ * Generate embeddings using a {@link OllamaEmbedRequest}.
*
* @param modelRequest request for '/api/embed' endpoint
* @return embeddings
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
+ * @throws OllamaException if the response indicates an error status
*/
- public OllamaEmbedResponseModel embed(OllamaEmbedRequestModel modelRequest)
- throws IOException, InterruptedException, OllamaBaseException {
- URI uri = URI.create(this.host + "/api/embed");
- String jsonData = Utils.getObjectMapper().writeValueAsString(modelRequest);
- HttpClient httpClient = HttpClient.newHttpClient();
-
- HttpRequest request = HttpRequest.newBuilder(uri)
- .header(Constants.HttpConstants.HEADER_KEY_ACCEPT, Constants.HttpConstants.APPLICATION_JSON)
- .POST(HttpRequest.BodyPublishers.ofString(jsonData)).build();
-
- HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
- int statusCode = response.statusCode();
- String responseBody = response.body();
-
- if (statusCode == 200) {
- return Utils.getObjectMapper().readValue(responseBody, OllamaEmbedResponseModel.class);
- } else {
- throw new OllamaBaseException(statusCode + " - " + responseBody);
- }
- }
-
- /**
- * Generate response for a question to a model running on Ollama server. This is
- * a sync/blocking call. This API does not support "thinking" models.
- *
- * @param model the ollama model to ask the question to
- * @param prompt the prompt/question text
- * @param raw if true no formatting will be applied to the
- * prompt. You
- * may choose to use the raw parameter if you are
- * specifying a full templated prompt in your
- * request to
- * the API
- * @param options the Options object - More
- * details on the options
- * @param responseStreamHandler optional callback consumer that will be applied
- * every
- * time a streamed response is received. If not
- * set, the
- * stream parameter of the request is set to false.
- * @return OllamaResult that includes response text and time taken for response
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- */
- public OllamaResult generate(String model, String prompt, boolean raw, Options options,
- OllamaStreamHandler responseStreamHandler) throws OllamaBaseException, IOException, InterruptedException {
- OllamaGenerateRequest ollamaRequestModel = new OllamaGenerateRequest(model, prompt);
- ollamaRequestModel.setRaw(raw);
- ollamaRequestModel.setThink(false);
- ollamaRequestModel.setOptions(options.getOptionsMap());
- return generateSyncForOllamaRequestModel(ollamaRequestModel, null, responseStreamHandler);
- }
-
- /**
- * Generate thinking and response tokens for a question to a thinking model
- * running on Ollama server. This is
- * a sync/blocking call.
- *
- * @param model the ollama model to ask the question to
- * @param prompt the prompt/question text
- * @param raw if true no formatting will be applied to the
- * prompt. You
- * may choose to use the raw parameter if you are
- * specifying a full templated prompt in your
- * request to
- * the API
- * @param options the Options object - More
- * details on the options
- * @param responseStreamHandler optional callback consumer that will be applied
- * every
- * time a streamed response is received. If not
- * set, the
- * stream parameter of the request is set to false.
- * @return OllamaResult that includes response text and time taken for response
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- */
- public OllamaResult generate(String model, String prompt, boolean raw, Options options,
- OllamaStreamHandler thinkingStreamHandler, OllamaStreamHandler responseStreamHandler)
- throws OllamaBaseException, IOException, InterruptedException {
- OllamaGenerateRequest ollamaRequestModel = new OllamaGenerateRequest(model, prompt);
- ollamaRequestModel.setRaw(raw);
- ollamaRequestModel.setThink(true);
- ollamaRequestModel.setOptions(options.getOptionsMap());
- return generateSyncForOllamaRequestModel(ollamaRequestModel, thinkingStreamHandler, responseStreamHandler);
- }
-
- /**
- * Generates response using the specified AI model and prompt (in blocking
- * mode).
- *
- * Uses
- * {@link #generate(String, String, boolean, Options, OllamaStreamHandler)}
- *
- * @param model The name or identifier of the AI model to use for generating
- * the response.
- * @param prompt The input text or prompt to provide to the AI model.
- * @param raw In some cases, you may wish to bypass the templating system
- * and provide a full prompt. In this case, you can use the raw
- * parameter to disable templating. Also note that raw mode will
- * not return a context.
- * @param options Additional options or configurations to use when generating
- * the response.
- * @param think if true the model will "think" step-by-step before
- * generating the final response
- * @return {@link OllamaResult}
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- */
- public OllamaResult generate(String model, String prompt, boolean raw, boolean think, Options options)
- throws OllamaBaseException, IOException, InterruptedException {
- if (think) {
- return generate(model, prompt, raw, options, null, null);
- } else {
- return generate(model, prompt, raw, options, null);
- }
- }
-
- /**
- * Generates structured output from the specified AI model and prompt.
- *
- * Note: When formatting is specified, the 'think' parameter is not allowed.
- *
- * @param model The name or identifier of the AI model to use for generating
- * the response.
- * @param prompt The input text or prompt to provide to the AI model.
- * @param format A map containing the format specification for the structured
- * output.
- * @return An instance of {@link OllamaResult} containing the structured
- * response.
- * @throws OllamaBaseException if the response indicates an error status.
- * @throws IOException if an I/O error occurs during the HTTP request.
- * @throws InterruptedException if the operation is interrupted.
- */
- @SuppressWarnings("LoggingSimilarMessage")
- public OllamaResult generate(String model, String prompt, Map format)
- throws OllamaBaseException, IOException, InterruptedException {
- URI uri = URI.create(this.host + "/api/generate");
-
- Map requestBody = new HashMap<>();
- requestBody.put("model", model);
- requestBody.put("prompt", prompt);
- requestBody.put("stream", false);
- requestBody.put("format", format);
-
- String jsonData = Utils.getObjectMapper().writeValueAsString(requestBody);
- HttpClient httpClient = HttpClient.newHttpClient();
-
- HttpRequest request = getRequestBuilderDefault(uri)
- .header(Constants.HttpConstants.HEADER_KEY_ACCEPT, Constants.HttpConstants.APPLICATION_JSON)
- .header(Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE, Constants.HttpConstants.APPLICATION_JSON)
- .POST(HttpRequest.BodyPublishers.ofString(jsonData)).build();
-
+ public OllamaEmbedResult embed(OllamaEmbedRequest modelRequest) throws OllamaException {
+ long startTime = System.currentTimeMillis();
+ String url = "/api/embed";
+ int statusCode = -1;
+ Object out = null;
try {
- String prettyJson = Utils.getObjectMapper().writerWithDefaultPrettyPrinter()
- .writeValueAsString(Utils.getObjectMapper().readValue(jsonData, Object.class));
- LOG.debug("Asking model:\n{}", prettyJson);
+ String jsonData = Utils.getObjectMapper().writeValueAsString(modelRequest);
+ HttpClient httpClient = HttpClient.newHttpClient();
+ HttpRequest request =
+ HttpRequest.newBuilder(new URI(this.host + url))
+ .header(
+ Constants.HttpConstants.HEADER_KEY_ACCEPT,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .POST(HttpRequest.BodyPublishers.ofString(jsonData))
+ .build();
+ HttpResponse response =
+ httpClient.send(request, HttpResponse.BodyHandlers.ofString());
+ statusCode = response.statusCode();
+ String responseBody = response.body();
+ if (statusCode == 200) {
+ return Utils.getObjectMapper().readValue(responseBody, OllamaEmbedResult.class);
+ } else {
+ throw new OllamaException(statusCode + " - " + responseBody);
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new OllamaException("Thread was interrupted", e);
} catch (Exception e) {
- LOG.debug("Asking model: {}", jsonData);
- }
-
- HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
- int statusCode = response.statusCode();
- String responseBody = response.body();
- if (statusCode == 200) {
- OllamaStructuredResult structuredResult = Utils.getObjectMapper().readValue(responseBody,
- OllamaStructuredResult.class);
- OllamaResult ollamaResult = new OllamaResult(structuredResult.getResponse(), structuredResult.getThinking(),
- structuredResult.getResponseTime(), statusCode);
-
- ollamaResult.setModel(structuredResult.getModel());
- ollamaResult.setCreatedAt(structuredResult.getCreatedAt());
- ollamaResult.setDone(structuredResult.isDone());
- ollamaResult.setDoneReason(structuredResult.getDoneReason());
- ollamaResult.setContext(structuredResult.getContext());
- ollamaResult.setTotalDuration(structuredResult.getTotalDuration());
- ollamaResult.setLoadDuration(structuredResult.getLoadDuration());
- ollamaResult.setPromptEvalCount(structuredResult.getPromptEvalCount());
- ollamaResult.setPromptEvalDuration(structuredResult.getPromptEvalDuration());
- ollamaResult.setEvalCount(structuredResult.getEvalCount());
- ollamaResult.setEvalDuration(structuredResult.getEvalDuration());
- LOG.debug("Model response:\n{}", ollamaResult);
- return ollamaResult;
- } else {
- LOG.debug("Model response:\n{}",
- Utils.getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(responseBody));
- throw new OllamaBaseException(statusCode + " - " + responseBody);
+ throw new OllamaException(e.getMessage(), e);
+ } finally {
+ MetricsRecorder.record(
+ url, "", false, false, false, null, null, startTime, statusCode, out);
}
}
/**
- * Generates response using the specified AI model and prompt (in blocking
- * mode), and then invokes a set of tools
- * on the generated response.
+ * 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 model The name or identifier of the AI model to use for generating
- * the response.
- * @param prompt The input text or prompt to provide to the AI model.
- * @param options Additional options or configurations to use when generating
- * the response.
- * @return {@link OllamaToolsResult} An OllamaToolsResult object containing the
- * response from the AI model and the results of invoking the tools on
- * that output.
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
+ * @param request the generation request
+ * @param streamObserver the stream observer for streaming responses, or null for synchronous
+ * @return the result of the generation
+ * @throws OllamaException if the request fails
*/
- public OllamaToolsResult generateWithTools(String model, String prompt, Options options)
- throws OllamaBaseException, IOException, InterruptedException, ToolInvocationException {
- boolean raw = true;
- OllamaToolsResult toolResult = new OllamaToolsResult();
- Map toolResults = new HashMap<>();
-
- if (!prompt.startsWith("[AVAILABLE_TOOLS]")) {
- final Tools.PromptBuilder promptBuilder = new Tools.PromptBuilder();
- for (Tools.ToolSpecification spec : toolRegistry.getRegisteredSpecs()) {
- promptBuilder.withToolSpecification(spec);
+ public OllamaResult generate(
+ OllamaGenerateRequest request, OllamaGenerateStreamObserver streamObserver)
+ throws OllamaException {
+ try {
+ if (request.isUseTools()) {
+ return generateWithToolsInternal(request, streamObserver);
}
- promptBuilder.withPrompt(prompt);
- prompt = promptBuilder.build();
- }
- OllamaResult result = generate(model, prompt, raw, options, null);
- toolResult.setModelResult(result);
-
- String toolsResponse = result.getResponse();
- if (toolsResponse.contains("[TOOL_CALLS]")) {
- toolsResponse = toolsResponse.replace("[TOOL_CALLS]", "");
- }
-
- List toolFunctionCallSpecs = new ArrayList<>();
- ObjectMapper objectMapper = Utils.getObjectMapper();
-
- if (!toolsResponse.isEmpty()) {
- try {
- // Try to parse the string to see if it's a valid JSON
- objectMapper.readTree(toolsResponse);
- } catch (JsonParseException e) {
- LOG.warn("Response from model does not contain any tool calls. Returning the response as is.");
- return toolResult;
+ if (streamObserver != null) {
+ if (request.isThink()) {
+ return generateSyncForOllamaRequestModel(
+ request,
+ streamObserver.getThinkingStreamHandler(),
+ streamObserver.getResponseStreamHandler());
+ } else {
+ return generateSyncForOllamaRequestModel(
+ request, null, streamObserver.getResponseStreamHandler());
+ }
}
- toolFunctionCallSpecs = objectMapper.readValue(toolsResponse,
- objectMapper.getTypeFactory().constructCollectionType(List.class, ToolFunctionCallSpec.class));
+ return generateSyncForOllamaRequestModel(request, null, null);
+ } catch (Exception e) {
+ throw new OllamaException(e.getMessage(), e);
}
- for (ToolFunctionCallSpec toolFunctionCallSpec : toolFunctionCallSpecs) {
- toolResults.put(toolFunctionCallSpec, invokeTool(toolFunctionCallSpec));
+ }
+
+ // (No javadoc for private helper, as is standard)
+ private OllamaResult generateWithToolsInternal(
+ OllamaGenerateRequest request, OllamaGenerateStreamObserver streamObserver)
+ throws OllamaException {
+ ArrayList msgs = new ArrayList<>();
+ OllamaChatRequest chatRequest = new OllamaChatRequest();
+ chatRequest.setModel(request.getModel());
+ OllamaChatMessage ocm = new OllamaChatMessage();
+ ocm.setRole(OllamaChatMessageRole.USER);
+ ocm.setResponse(request.getPrompt());
+ chatRequest.setMessages(msgs);
+ msgs.add(ocm);
+ OllamaChatTokenHandler hdlr = null;
+ chatRequest.setTools(request.getTools());
+ if (streamObserver != null) {
+ chatRequest.setStream(true);
+ if (streamObserver.getResponseStreamHandler() != null) {
+ hdlr =
+ chatResponseModel ->
+ streamObserver
+ .getResponseStreamHandler()
+ .accept(chatResponseModel.getMessage().getResponse());
+ }
}
- toolResult.setToolResults(toolResults);
- return toolResult;
+ OllamaChatResult res = chat(chatRequest, hdlr);
+ return new OllamaResult(
+ res.getResponseModel().getMessage().getResponse(),
+ res.getResponseModel().getMessage().getThinking(),
+ res.getResponseModel().getTotalDuration(),
+ -1);
}
/**
- * Asynchronously generates a response for a prompt using a model running on the
- * Ollama server.
- *
- * This method returns an {@link OllamaAsyncResultStreamer} handle that can be
- * used to poll for
- * status and retrieve streamed "thinking" and response tokens from the model.
- * The call is non-blocking.
- *
+ * Generates a response from a model asynchronously, returning a streamer for results.
*
- *
- *
- * @param model the Ollama model to use for generating the response
- * @param prompt the prompt or question text to send to the model
- * @param raw if {@code true}, returns the raw response from the model
- * @param think if {@code true}, streams "thinking" tokens as well as response
- * tokens
- * @return an {@link OllamaAsyncResultStreamer} handle for polling and
- * retrieving streamed 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 OllamaException if the request fails
*/
- public OllamaAsyncResultStreamer generateAsync(String model, String prompt, boolean raw, boolean think) {
- OllamaGenerateRequest ollamaRequestModel = new OllamaGenerateRequest(model, prompt);
- ollamaRequestModel.setRaw(raw);
- ollamaRequestModel.setThink(think);
- URI uri = URI.create(this.host + "/api/generate");
- OllamaAsyncResultStreamer ollamaAsyncResultStreamer = new OllamaAsyncResultStreamer(
- getRequestBuilderDefault(uri), ollamaRequestModel, requestTimeoutSeconds);
- ollamaAsyncResultStreamer.start();
- return ollamaAsyncResultStreamer;
- }
-
- /**
- * With one or more image files, ask a question to a model running on Ollama
- * server. This is a
- * sync/blocking call.
- *
- * @param model the ollama model to ask the question to
- * @param prompt the prompt/question text
- * @param imageFiles the list of image files to use for the question
- * @param options the Options object - More
- * details on the options
- * @param streamHandler optional callback consumer that will be applied every
- * time a streamed response is received. If not set, the
- * stream parameter of the request is set to false.
- * @return OllamaResult that includes response text and time taken for response
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- */
- public OllamaResult generateWithImageFiles(String model, String prompt, List imageFiles, Options options,
- OllamaStreamHandler streamHandler) throws OllamaBaseException, IOException, InterruptedException {
- List images = new ArrayList<>();
- for (File imageFile : imageFiles) {
- images.add(encodeFileToBase64(imageFile));
+ public OllamaAsyncResultStreamer generateAsync(
+ String model, String prompt, boolean raw, boolean think) throws OllamaException {
+ long startTime = System.currentTimeMillis();
+ String url = "/api/generate";
+ int statusCode = -1;
+ try {
+ OllamaGenerateRequest ollamaRequestModel = new OllamaGenerateRequest(model, prompt);
+ ollamaRequestModel.setRaw(raw);
+ ollamaRequestModel.setThink(think);
+ OllamaAsyncResultStreamer ollamaAsyncResultStreamer =
+ new OllamaAsyncResultStreamer(
+ getRequestBuilderDefault(new URI(this.host + url)),
+ ollamaRequestModel,
+ requestTimeoutSeconds);
+ ollamaAsyncResultStreamer.start();
+ statusCode = ollamaAsyncResultStreamer.getHttpStatusCode();
+ return ollamaAsyncResultStreamer;
+ } catch (Exception e) {
+ throw new OllamaException(e.getMessage(), e);
+ } finally {
+ MetricsRecorder.record(
+ url, model, raw, think, true, null, null, startTime, statusCode, null);
}
- OllamaGenerateRequest ollamaRequestModel = new OllamaGenerateRequest(model, prompt, images);
- ollamaRequestModel.setOptions(options.getOptionsMap());
- return generateSyncForOllamaRequestModel(ollamaRequestModel, null, streamHandler);
}
/**
- * Convenience method to call Ollama API without streaming responses.
- *
- * Uses
- * {@link #generateWithImageFiles(String, String, List, Options, OllamaStreamHandler)}
+ * Sends a chat request to a model using an {@link OllamaChatRequest} and sets up streaming response.
+ * This can be constructed using an {@link OllamaChatRequestBuilder}.
*
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- */
- public OllamaResult generateWithImageFiles(String model, String prompt, List imageFiles, Options options)
- throws OllamaBaseException, IOException, InterruptedException {
- return generateWithImageFiles(model, prompt, imageFiles, options, null);
- }
-
- /**
- * With one or more image URLs, ask a question to a model running on Ollama
- * server. This is a
- * sync/blocking call.
- *
- * @param model the ollama model to ask the question to
- * @param prompt the prompt/question text
- * @param imageURLs the list of image URLs to use for the question
- * @param options the Options object - More
- * details on the options
- * @param streamHandler optional callback consumer that will be applied every
- * time a streamed response is received. If not set, the
- * stream parameter of the request is set to false.
- * @return OllamaResult that includes response text and time taken for response
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- * @throws URISyntaxException if the URI for the request is malformed
- */
- public OllamaResult generateWithImageURLs(String model, String prompt, List imageURLs, Options options,
- OllamaStreamHandler streamHandler)
- throws OllamaBaseException, IOException, InterruptedException, URISyntaxException {
- List images = new ArrayList<>();
- for (String imageURL : imageURLs) {
- images.add(encodeByteArrayToBase64(Utils.loadImageBytesFromUrl(imageURL)));
- }
- OllamaGenerateRequest ollamaRequestModel = new OllamaGenerateRequest(model, prompt, images);
- ollamaRequestModel.setOptions(options.getOptionsMap());
- return generateSyncForOllamaRequestModel(ollamaRequestModel, null, streamHandler);
- }
-
- /**
- * Convenience method to call Ollama API without streaming responses.
- *
- * Uses
- * {@link #generateWithImageURLs(String, String, List, Options, OllamaStreamHandler)}
- *
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- * @throws URISyntaxException if the URI for the request is malformed
- */
- public OllamaResult generateWithImageURLs(String model, String prompt, List imageURLs, Options options)
- throws OllamaBaseException, IOException, InterruptedException, URISyntaxException {
- return generateWithImageURLs(model, prompt, imageURLs, options, null);
- }
-
- /**
- * Synchronously generates a response using a list of image byte arrays.
- *
- * This method encodes the provided byte arrays into Base64 and sends them to
- * the Ollama server.
- *
- * @param model the Ollama model to use for generating the response
- * @param prompt the prompt or question text to send to the model
- * @param images the list of image data as byte arrays
- * @param options the Options object - More
- * details on the options
- * @param streamHandler optional callback that will be invoked with each
- * streamed response; if null, streaming is disabled
- * @return OllamaResult containing the response text and the time taken for the
- * response
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- */
- public OllamaResult generateWithImages(String model, String prompt, List images, Options options,
- OllamaStreamHandler streamHandler) throws OllamaBaseException, IOException, InterruptedException {
- List encodedImages = new ArrayList<>();
- for (byte[] image : images) {
- encodedImages.add(encodeByteArrayToBase64(image));
- }
- OllamaGenerateRequest ollamaRequestModel = new OllamaGenerateRequest(model, prompt, encodedImages);
- ollamaRequestModel.setOptions(options.getOptionsMap());
- return generateSyncForOllamaRequestModel(ollamaRequestModel, null, streamHandler);
- }
-
- /**
- * Convenience method to call the Ollama API using image byte arrays without
- * streaming responses.
- *
- * Uses
- * {@link #generateWithImages(String, String, List, Options, OllamaStreamHandler)}
- *
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- */
- public OllamaResult generateWithImages(String model, String prompt, List images, Options options)
- throws OllamaBaseException, IOException, InterruptedException {
- return generateWithImages(model, prompt, images, options, null);
- }
-
- /**
- * Ask a question to a model based on a given message stack (i.e. a chat
- * history). Creates a synchronous call to the api
- * 'api/chat'.
- *
- * @param model the ollama model to ask the question to
- * @param messages chat history / message stack to send to the model
- * @return {@link OllamaChatResult} containing the api response and the message
- * history including the newly acquired assistant response.
- * @throws OllamaBaseException any response code than 200 has been returned
- * @throws IOException in case the responseStream can not be read
- * @throws InterruptedException in case the server is not reachable or
- * network
- * issues happen
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP
- * request
- * @throws InterruptedException if the operation is interrupted
- * @throws ToolInvocationException if the tool invocation fails
- */
- public OllamaChatResult chat(String model, List messages)
- throws OllamaBaseException, IOException, InterruptedException, ToolInvocationException {
- OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(model);
- return chat(builder.withMessages(messages).build());
- }
-
- /**
- * Ask a question to a model using an {@link OllamaChatRequest}. 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
+ * previous tokens from stream will not be concatenated)
* @return {@link OllamaChatResult}
- * @throws OllamaBaseException any response code than 200 has been returned
- * @throws IOException in case the responseStream can not be read
- * @throws InterruptedException in case the server is not reachable or
- * network
- * issues happen
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP
- * request
- * @throws InterruptedException if the operation is interrupted
- * @throws ToolInvocationException if the tool invocation fails
+ * @throws OllamaException if the response indicates an error status
*/
- public OllamaChatResult chat(OllamaChatRequest request)
- throws OllamaBaseException, IOException, InterruptedException, ToolInvocationException {
- return chat(request, null, null);
- }
+ public OllamaChatResult chat(OllamaChatRequest request, OllamaChatTokenHandler tokenHandler)
+ throws OllamaException {
+ try {
+ OllamaChatEndpointCaller requestCaller =
+ new OllamaChatEndpointCaller(host, auth, requestTimeoutSeconds);
+ OllamaChatResult result;
- /**
- * Ask a question to a model using an {@link OllamaChatRequest}. This can be
- * constructed using an {@link OllamaChatRequestBuilder}.
- *
- * Hint: the OllamaChatRequestModel#getStream() property is not implemented.
- *
- * @param request request object to be sent to the server
- * @param responseStreamHandler callback handler to handle the last message from
- * stream
- * @param thinkingStreamHandler callback handler to handle the last thinking
- * message from stream
- * @return {@link OllamaChatResult}
- * @throws OllamaBaseException any response code than 200 has been returned
- * @throws IOException in case the responseStream can not be read
- * @throws InterruptedException in case the server is not reachable or
- * network
- * issues happen
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP
- * request
- * @throws InterruptedException if the operation is interrupted
- * @throws ToolInvocationException if the tool invocation fails
- */
- public OllamaChatResult chat(OllamaChatRequest request, OllamaStreamHandler thinkingStreamHandler,
- OllamaStreamHandler responseStreamHandler)
- throws OllamaBaseException, IOException, InterruptedException, ToolInvocationException {
- return chatStreaming(request, new OllamaChatStreamObserver(thinkingStreamHandler, responseStreamHandler));
- }
-
- /**
- * Ask a question to a model using an {@link OllamaChatRequest}. This can be
- * constructed using an {@link OllamaChatRequestBuilder}.
- *
- * Hint: 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 previous tokens from stream will not be
- * concatenated)
- * @return {@link OllamaChatResult}
- * @throws OllamaBaseException any response code than 200 has been returned
- * @throws IOException in case the responseStream can not be read
- * @throws InterruptedException in case the server is not reachable or network
- * issues happen
- * @throws OllamaBaseException if the response indicates an error status
- * @throws IOException if an I/O error occurs during the HTTP request
- * @throws InterruptedException if the operation is interrupted
- */
- public OllamaChatResult chatStreaming(OllamaChatRequest request, OllamaTokenHandler tokenHandler)
- throws OllamaBaseException, IOException, InterruptedException, ToolInvocationException {
- OllamaChatEndpointCaller requestCaller = new OllamaChatEndpointCaller(host, auth, requestTimeoutSeconds);
- OllamaChatResult result;
-
- // add all registered tools to Request
- request.setTools(toolRegistry.getRegisteredSpecs().stream().map(Tools.ToolSpecification::getToolPrompt)
- .collect(Collectors.toList()));
-
- if (tokenHandler != null) {
- request.setStream(true);
- result = requestCaller.call(request, tokenHandler);
- } else {
- result = requestCaller.callSync(request);
- }
-
- if (clientHandlesTools) {
- return result;
- }
-
- // check if toolCallIsWanted
- List toolCalls = result.getResponseModel().getMessage().getToolCalls();
- int toolCallTries = 0;
- while (toolCalls != null && !toolCalls.isEmpty() && toolCallTries < maxChatToolCallRetries) {
- for (OllamaChatToolCalls toolCall : toolCalls) {
- String toolName = toolCall.getFunction().getName();
- ToolFunction toolFunction = toolRegistry.getToolFunction(toolName);
- if (toolFunction == null) {
- throw new ToolInvocationException("Tool function not found: " + toolName);
- }
- Map arguments = toolCall.getFunction().getArguments();
- Object res = toolFunction.apply(arguments);
- String argumentKeys = arguments.keySet().stream()
- .map(Object::toString)
- .collect(Collectors.joining(", "));
- request.getMessages().add(new OllamaChatMessage(OllamaChatMessageRole.TOOL,
- "[TOOL_RESULTS] " + toolName + "(" + argumentKeys + "): " + res + " [/TOOL_RESULTS]"));
+ // only add tools if tools flag is set
+ if (request.isUseTools()) {
+ // add all registered tools to request
+ request.setTools(toolRegistry.getRegisteredTools());
}
if (tokenHandler != null) {
+ request.setStream(true);
result = requestCaller.call(request, tokenHandler);
} else {
result = requestCaller.callSync(request);
}
- toolCalls = result.getResponseModel().getMessage().getToolCalls();
- toolCallTries++;
- }
- return result;
- }
-
- /**
- * Registers a single tool in the tool registry using the provided tool
- * specification.
- *
- * @param toolSpecification the specification of the tool to register. It
- * contains the
- * tool's function name and other relevant information.
- */
- public void registerTool(Tools.ToolSpecification toolSpecification) {
- toolRegistry.addTool(toolSpecification.getFunctionName(), toolSpecification);
- LOG.debug("Registered tool: {}", toolSpecification.getFunctionName());
- }
-
- /**
- * Registers multiple tools in the tool registry using a list of tool
- * specifications.
- * Iterates over the list and adds each tool specification to the registry.
- *
- * @param toolSpecifications a list of tool specifications to register. Each
- * specification
- * contains information about a tool, such as its
- * function name.
- */
- public void registerTools(List toolSpecifications) {
- for (Tools.ToolSpecification toolSpecification : toolSpecifications) {
- toolRegistry.addTool(toolSpecification.getFunctionName(), toolSpecification);
+ // check if toolCallIsWanted
+ List toolCalls =
+ result.getResponseModel().getMessage().getToolCalls();
+ int toolCallTries = 0;
+ while (toolCalls != null
+ && !toolCalls.isEmpty()
+ && toolCallTries < maxChatToolCallRetries) {
+ for (OllamaChatToolCalls toolCall : toolCalls) {
+ String toolName = toolCall.getFunction().getName();
+ for (Tools.Tool t : request.getTools()) {
+ if (t.getToolSpec().getName().equals(toolName)) {
+ ToolFunction toolFunction = t.getToolFunction();
+ if (toolFunction == null) {
+ throw new ToolInvocationException(
+ "Tool function not found: " + toolName);
+ }
+ LOG.debug(
+ "Invoking tool {} with arguments: {}",
+ toolCall.getFunction().getName(),
+ toolCall.getFunction().getArguments());
+ Map arguments = toolCall.getFunction().getArguments();
+ Object res = toolFunction.apply(arguments);
+ String argumentKeys =
+ arguments.keySet().stream()
+ .map(Object::toString)
+ .collect(Collectors.joining(", "));
+ request.getMessages()
+ .add(
+ new OllamaChatMessage(
+ OllamaChatMessageRole.TOOL,
+ "[TOOL_RESULTS] "
+ + toolName
+ + "("
+ + argumentKeys
+ + "): "
+ + res
+ + " [/TOOL_RESULTS]"));
+ }
+ }
+ }
+ if (tokenHandler != null) {
+ result = requestCaller.call(request, tokenHandler);
+ } else {
+ result = requestCaller.callSync(request);
+ }
+ toolCalls = result.getResponseModel().getMessage().getToolCalls();
+ toolCallTries++;
+ }
+ return result;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new OllamaException("Thread was interrupted", e);
+ } catch (Exception e) {
+ throw new OllamaException(e.getMessage(), e);
}
}
/**
- * Deregisters all tools from the tool registry.
- * This method removes all registered tools, effectively clearing the registry.
+ * Registers a single tool in the tool registry.
+ *
+ * @param tool the tool to register. Contains the tool's specification and function.
+ */
+ public void registerTool(Tools.Tool tool) {
+ toolRegistry.addTool(tool);
+ LOG.debug("Registered tool: {}", tool.getToolSpec().getName());
+ }
+
+ /**
+ * 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.
+ */
+ public void registerTools(List tools) {
+ toolRegistry.addTools(tools);
+ }
+
+ /**
+ * Deregisters all tools from the tool registry. This method removes all registered tools,
+ * effectively clearing the registry.
*/
public void deregisterTools() {
toolRegistry.clear();
@@ -1432,53 +974,50 @@ public class OllamaAPI {
}
/**
- * 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.
+ * 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 IllegalStateException if the caller's class is not annotated with
- * {@link OllamaToolService}.
- * @throws RuntimeException if any reflection-based instantiation or
- * invocation fails.
+ * @throws OllamaException if the caller's class is not annotated with {@link
+ * OllamaToolService} or if reflection-based instantiation or invocation fails
*/
- public void registerAnnotatedTools() {
+ public void registerAnnotatedTools() throws OllamaException {
try {
Class> callerClass = null;
try {
- callerClass = Class.forName(Thread.currentThread().getStackTrace()[2].getClassName());
+ callerClass =
+ Class.forName(Thread.currentThread().getStackTrace()[2].getClassName());
} catch (ClassNotFoundException e) {
- throw new RuntimeException(e);
+ throw new OllamaException(e.getMessage(), e);
}
- OllamaToolService ollamaToolServiceAnnotation = callerClass.getDeclaredAnnotation(OllamaToolService.class);
+ OllamaToolService ollamaToolServiceAnnotation =
+ callerClass.getDeclaredAnnotation(OllamaToolService.class);
if (ollamaToolServiceAnnotation == null) {
- throw new IllegalStateException(callerClass + " is not annotated as " + OllamaToolService.class);
+ 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 RuntimeException(e);
+ } catch (InstantiationException
+ | NoSuchMethodException
+ | IllegalAccessException
+ | InvocationTargetException e) {
+ throw new OllamaException(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.
+ * 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.
+ * @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();
@@ -1491,39 +1030,43 @@ public class OllamaAPI {
String operationName = !toolSpec.name().isBlank() ? toolSpec.name() : m.getName();
String operationDesc = !toolSpec.desc().isBlank() ? toolSpec.desc() : operationName;
- final Tools.PropsBuilder propsBuilder = new Tools.PropsBuilder();
+ final Map params = new HashMap() {};
LinkedHashMap methodParams = new LinkedHashMap<>();
for (Parameter parameter : m.getParameters()) {
- final ToolProperty toolPropertyAnn = parameter.getDeclaredAnnotation(ToolProperty.class);
+ 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();
+ 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());
+ params.put(
+ propName,
+ Tools.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);
+ 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());
}
-
}
/**
@@ -1550,8 +1093,7 @@ public class OllamaAPI {
*
* @param roleName the name of the role to retrieve
* @return the OllamaChatMessageRole associated with the given name
- * @throws RoleNotFoundException if the role with the specified name does not
- * exist
+ * @throws RoleNotFoundException if the role with the specified name does not exist
*/
public OllamaChatMessageRole getRole(String roleName) throws RoleNotFoundException {
return OllamaChatMessageRole.getRole(roleName);
@@ -1562,9 +1104,9 @@ public class OllamaAPI {
/**
* Utility method to encode a file into a Base64 encoded string.
*
- * @param file the file to be encoded into Base64.
- * @return a Base64 encoded string representing the contents of the file.
- * @throws IOException if an I/O error occurs during reading the file.
+ * @param file the file to be encoded into Base64
+ * @return a Base64 encoded string representing the contents of the file
+ * @throws IOException if an I/O error occurs during reading the file
*/
private static String encodeFileToBase64(File file) throws IOException {
return Base64.getEncoder().encodeToString(Files.readAllBytes(file.toPath()));
@@ -1573,8 +1115,8 @@ public class OllamaAPI {
/**
* Utility method to encode a byte array into a Base64 encoded string.
*
- * @param bytes the byte array to be encoded into Base64.
- * @return a Base64 encoded string representing the byte array.
+ * @param bytes the byte array to be encoded into Base64
+ * @return a Base64 encoded string representing the byte array
*/
private static String encodeByteArrayToBase64(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
@@ -1582,36 +1124,56 @@ 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.
+ * 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 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.
+ * @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
+ * @return the result of the Ollama API request
+ * @throws OllamaException if the request fails due to an issue with the Ollama API
*/
- private OllamaResult generateSyncForOllamaRequestModel(OllamaGenerateRequest ollamaRequestModel,
- OllamaStreamHandler thinkingStreamHandler, OllamaStreamHandler responseStreamHandler)
- throws OllamaBaseException, IOException, InterruptedException {
- OllamaGenerateEndpointCaller requestCaller = new OllamaGenerateEndpointCaller(host, auth, requestTimeoutSeconds);
- OllamaResult result;
- if (responseStreamHandler != null) {
- ollamaRequestModel.setStream(true);
- result = requestCaller.call(ollamaRequestModel, thinkingStreamHandler, responseStreamHandler);
- } else {
- result = requestCaller.callSync(ollamaRequestModel);
+ private OllamaResult generateSyncForOllamaRequestModel(
+ OllamaGenerateRequest ollamaRequestModel,
+ OllamaGenerateTokenHandler thinkingStreamHandler,
+ OllamaGenerateTokenHandler responseStreamHandler)
+ throws OllamaException {
+ long startTime = System.currentTimeMillis();
+ int statusCode = -1;
+ Object out = null;
+ try {
+ OllamaGenerateEndpointCaller requestCaller =
+ new OllamaGenerateEndpointCaller(host, auth, requestTimeoutSeconds);
+ OllamaResult result;
+ if (responseStreamHandler != null) {
+ ollamaRequestModel.setStream(true);
+ result =
+ requestCaller.call(
+ ollamaRequestModel, thinkingStreamHandler, responseStreamHandler);
+ } else {
+ result = requestCaller.callSync(ollamaRequestModel);
+ }
+ statusCode = result.getHttpStatusCode();
+ out = result;
+ return result;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new OllamaException("Thread was interrupted", e);
+ } catch (Exception e) {
+ throw new OllamaException(e.getMessage(), e);
+ } finally {
+ MetricsRecorder.record(
+ OllamaGenerateEndpointCaller.endpoint,
+ ollamaRequestModel.getModel(),
+ ollamaRequestModel.isRaw(),
+ ollamaRequestModel.isThink(),
+ ollamaRequestModel.isStream(),
+ ollamaRequestModel.getOptions(),
+ ollamaRequestModel.getFormat(),
+ startTime,
+ statusCode,
+ out);
}
- return result;
}
/**
@@ -1621,9 +1183,12 @@ public class OllamaAPI {
* @return HttpRequest.Builder
*/
private HttpRequest.Builder getRequestBuilderDefault(URI uri) {
- HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(uri)
- .header(Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE, Constants.HttpConstants.APPLICATION_JSON)
- .timeout(Duration.ofSeconds(requestTimeoutSeconds));
+ HttpRequest.Builder requestBuilder =
+ HttpRequest.newBuilder(uri)
+ .header(
+ Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE,
+ Constants.HttpConstants.APPLICATION_JSON)
+ .timeout(Duration.ofSeconds(requestTimeoutSeconds));
if (isAuthSet()) {
requestBuilder.header("Authorization", auth.getAuthHeaderValue());
}
@@ -1638,20 +1203,4 @@ public class OllamaAPI {
private boolean isAuthSet() {
return auth != null;
}
-
- 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);
- }
- }
}
diff --git a/src/main/java/io/github/ollama4j/exceptions/OllamaBaseException.java b/src/main/java/io/github/ollama4j/exceptions/OllamaBaseException.java
deleted file mode 100644
index 9474d72..0000000
--- a/src/main/java/io/github/ollama4j/exceptions/OllamaBaseException.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package io.github.ollama4j.exceptions;
-
-public class OllamaBaseException extends Exception {
-
- public OllamaBaseException(String s) {
- super(s);
- }
-}
diff --git a/src/main/java/io/github/ollama4j/exceptions/OllamaException.java b/src/main/java/io/github/ollama4j/exceptions/OllamaException.java
new file mode 100644
index 0000000..7570c10
--- /dev/null
+++ b/src/main/java/io/github/ollama4j/exceptions/OllamaException.java
@@ -0,0 +1,20 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
+package io.github.ollama4j.exceptions;
+
+public class OllamaException extends Exception {
+
+ public OllamaException(String message) {
+ super(message);
+ }
+
+ public OllamaException(String message, Exception exception) {
+ super(message, exception);
+ }
+}
diff --git a/src/main/java/io/github/ollama4j/exceptions/RoleNotFoundException.java b/src/main/java/io/github/ollama4j/exceptions/RoleNotFoundException.java
index a7d1d18..11c6370 100644
--- a/src/main/java/io/github/ollama4j/exceptions/RoleNotFoundException.java
+++ b/src/main/java/io/github/ollama4j/exceptions/RoleNotFoundException.java
@@ -1,3 +1,11 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.exceptions;
public class RoleNotFoundException extends Exception {
diff --git a/src/main/java/io/github/ollama4j/exceptions/ToolInvocationException.java b/src/main/java/io/github/ollama4j/exceptions/ToolInvocationException.java
index 4707e55..1bcb8f9 100644
--- a/src/main/java/io/github/ollama4j/exceptions/ToolInvocationException.java
+++ b/src/main/java/io/github/ollama4j/exceptions/ToolInvocationException.java
@@ -1,3 +1,11 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.exceptions;
public class ToolInvocationException extends Exception {
diff --git a/src/main/java/io/github/ollama4j/exceptions/ToolNotFoundException.java b/src/main/java/io/github/ollama4j/exceptions/ToolNotFoundException.java
index bd3e007..28e4b7f 100644
--- a/src/main/java/io/github/ollama4j/exceptions/ToolNotFoundException.java
+++ b/src/main/java/io/github/ollama4j/exceptions/ToolNotFoundException.java
@@ -1,3 +1,11 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.exceptions;
public class ToolNotFoundException extends Exception {
diff --git a/src/main/java/io/github/ollama4j/impl/ConsoleOutputChatTokenHandler.java b/src/main/java/io/github/ollama4j/impl/ConsoleOutputChatTokenHandler.java
new file mode 100644
index 0000000..ea0f728
--- /dev/null
+++ b/src/main/java/io/github/ollama4j/impl/ConsoleOutputChatTokenHandler.java
@@ -0,0 +1,18 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
+package io.github.ollama4j.impl;
+
+import io.github.ollama4j.models.chat.OllamaChatStreamObserver;
+
+public final class ConsoleOutputChatTokenHandler extends OllamaChatStreamObserver {
+ public ConsoleOutputChatTokenHandler() {
+ setThinkingStreamHandler(new ConsoleOutputGenerateTokenHandler());
+ setResponseStreamHandler(new ConsoleOutputGenerateTokenHandler());
+ }
+}
diff --git a/src/main/java/io/github/ollama4j/impl/ConsoleOutputGenerateTokenHandler.java b/src/main/java/io/github/ollama4j/impl/ConsoleOutputGenerateTokenHandler.java
new file mode 100644
index 0000000..b303315
--- /dev/null
+++ b/src/main/java/io/github/ollama4j/impl/ConsoleOutputGenerateTokenHandler.java
@@ -0,0 +1,18 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
+package io.github.ollama4j.impl;
+
+import io.github.ollama4j.models.generate.OllamaGenerateTokenHandler;
+
+public class ConsoleOutputGenerateTokenHandler implements OllamaGenerateTokenHandler {
+ @Override
+ public void accept(String message) {
+ System.out.print(message);
+ }
+}
diff --git a/src/main/java/io/github/ollama4j/impl/ConsoleOutputStreamHandler.java b/src/main/java/io/github/ollama4j/impl/ConsoleOutputStreamHandler.java
deleted file mode 100644
index b5b3da8..0000000
--- a/src/main/java/io/github/ollama4j/impl/ConsoleOutputStreamHandler.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package io.github.ollama4j.impl;
-
-import io.github.ollama4j.models.generate.OllamaStreamHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class ConsoleOutputStreamHandler implements OllamaStreamHandler {
- private static final Logger LOG = LoggerFactory.getLogger(ConsoleOutputStreamHandler.class);
-
- @Override
- public void accept(String message) {
- LOG.info(message);
- }
-}
diff --git a/src/main/java/io/github/ollama4j/metrics/MetricsRecorder.java b/src/main/java/io/github/ollama4j/metrics/MetricsRecorder.java
new file mode 100644
index 0000000..bfd6ef1
--- /dev/null
+++ b/src/main/java/io/github/ollama4j/metrics/MetricsRecorder.java
@@ -0,0 +1,129 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
+package io.github.ollama4j.metrics;
+
+import com.google.common.base.Throwables;
+import io.prometheus.client.Counter;
+import io.prometheus.client.Histogram;
+import java.util.Map;
+
+public class MetricsRecorder {
+
+ // Corrected: Removed duplicate "format" label and ensured label count matches usage
+ private static final Counter requests =
+ Counter.build()
+ .name("ollama_api_requests_total")
+ .help("Total requests to Ollama API")
+ .labelNames(
+ "endpoint",
+ "model",
+ "raw",
+ "streaming",
+ "thinking",
+ "http_status",
+ "options",
+ "format")
+ .register();
+
+ private static final Histogram requestLatency =
+ Histogram.build()
+ .name("ollama_api_request_duration_seconds")
+ .help("Request latency in seconds")
+ .labelNames(
+ "endpoint",
+ "model",
+ "raw",
+ "streaming",
+ "thinking",
+ "http_status",
+ "options",
+ "format")
+ .register();
+
+ private static final Histogram responseSize =
+ Histogram.build()
+ .name("ollama_api_response_size_bytes")
+ .help("Response size in bytes")
+ .labelNames("endpoint", "model", "options")
+ .register();
+
+ public static void record(
+ String endpoint,
+ String model,
+ boolean raw,
+ boolean thinking,
+ boolean streaming,
+ Map options,
+ Object format,
+ long startTime,
+ int responseHttpStatus,
+ Object response) {
+ long endTime = System.currentTimeMillis();
+
+ String httpStatus = String.valueOf(responseHttpStatus);
+
+ String formatString = "";
+ if (format instanceof String) {
+ formatString = (String) format;
+ } else if (format instanceof Map) {
+ formatString = mapToString((Map) format);
+ } else if (format != null) {
+ formatString = format.toString();
+ }
+
+ // Ensure the number of labels matches the labelNames above (8 labels)
+ requests.labels(
+ endpoint,
+ safe(model),
+ String.valueOf(raw),
+ String.valueOf(streaming),
+ String.valueOf(thinking),
+ httpStatus,
+ safe(mapToString(options)),
+ safe(formatString))
+ .inc();
+ double durationSeconds = (endTime - startTime) / 1000.0;
+
+ // Ensure the number of labels matches the labelNames above (8 labels)
+ requestLatency
+ .labels(
+ endpoint,
+ safe(model),
+ String.valueOf(raw),
+ String.valueOf(streaming),
+ String.valueOf(thinking),
+ httpStatus,
+ safe(mapToString(options)),
+ safe(formatString))
+ .observe(durationSeconds);
+
+ // Record response size (only if response is a string or json-like object)
+ if (response != null) {
+ if (response instanceof Exception) {
+ response = Throwables.getStackTraceAsString((Throwable) response);
+ }
+ int size = response.toString().length();
+ responseSize.labels(endpoint, safe(model), safe(mapToString(options))).observe(size);
+ }
+ }
+
+ // Utility method to convert options Map to string (you can adjust this for more detailed
+ // representation)
+ private static String mapToString(Map map) {
+ if (map == null || map.isEmpty()) {
+ return "none";
+ }
+ // Convert the map to a string (can be customized to fit the use case)
+ return map.toString();
+ }
+
+ private static String safe(String value) {
+ return (value == null || value.isEmpty()) ? "none" : value;
+ }
+}
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 e3d7912..ef1b3da 100644
--- a/src/main/java/io/github/ollama4j/models/chat/OllamaChatMessage.java
+++ b/src/main/java/io/github/ollama4j/models/chat/OllamaChatMessage.java
@@ -1,21 +1,31 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.chat;
+import static io.github.ollama4j.utils.Utils.getObjectMapper;
+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.github.ollama4j.utils.FileToBase64Serializer;
-import lombok.*;
-
import java.util.List;
-
-import static io.github.ollama4j.utils.Utils.getObjectMapper;
+import lombok.*;
/**
* Defines a single Message to be used inside a chat request against the ollama /api/chat endpoint.
*
- * @see Generate chat completion
+ * @see Generate
+ * chat completion
*/
+@SuppressWarnings("NullableProblems")
@Data
@AllArgsConstructor
@RequiredArgsConstructor
@@ -23,11 +33,11 @@ import static io.github.ollama4j.utils.Utils.getObjectMapper;
@JsonIgnoreProperties(ignoreUnknown = true)
public class OllamaChatMessage {
- @NonNull
- private OllamaChatMessageRole role;
+ @NonNull private OllamaChatMessageRole role;
+ @JsonProperty("content")
@NonNull
- private String content;
+ private String response;
private String thinking;
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 37d9d5c..617fb51 100644
--- a/src/main/java/io/github/ollama4j/models/chat/OllamaChatMessageRole.java
+++ b/src/main/java/io/github/ollama4j/models/chat/OllamaChatMessageRole.java
@@ -1,11 +1,18 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.chat;
import com.fasterxml.jackson.annotation.JsonValue;
import io.github.ollama4j.exceptions.RoleNotFoundException;
-import lombok.Getter;
-
import java.util.ArrayList;
import java.util.List;
+import lombok.Getter;
/**
* Defines the possible Chat Message roles.
@@ -19,8 +26,7 @@ public class OllamaChatMessageRole {
public static final OllamaChatMessageRole ASSISTANT = new OllamaChatMessageRole("assistant");
public static final OllamaChatMessageRole TOOL = new OllamaChatMessageRole("tool");
- @JsonValue
- private final String roleName;
+ @JsonValue private final String roleName;
private OllamaChatMessageRole(String roleName) {
this.roleName = roleName;
@@ -28,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/OllamaChatRequest.java b/src/main/java/io/github/ollama4j/models/chat/OllamaChatRequest.java
index 7b19e02..a10cf77 100644
--- a/src/main/java/io/github/ollama4j/models/chat/OllamaChatRequest.java
+++ b/src/main/java/io/github/ollama4j/models/chat/OllamaChatRequest.java
@@ -1,32 +1,50 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.chat;
import io.github.ollama4j.models.request.OllamaCommonRequest;
import io.github.ollama4j.tools.Tools;
import io.github.ollama4j.utils.OllamaRequestBody;
+import java.util.Collections;
+import java.util.List;
import lombok.Getter;
import lombok.Setter;
-import java.util.List;
-
/**
* Defines a Request to use against the ollama /api/chat endpoint.
*
* @see Generate
- * Chat Completion
+ * "https://github.com/ollama/ollama/blob/main/docs/api.md#generate-a-chat-completion">Generate
+ * Chat Completion
*/
@Getter
@Setter
public class OllamaChatRequest extends OllamaCommonRequest implements OllamaRequestBody {
- private List messages;
+ private List messages = Collections.emptyList();
- private List tools;
+ private List tools;
private boolean think;
- public OllamaChatRequest() {
- }
+ /**
+ * Controls whether tools are automatically executed.
+ *
+ *
If set to {@code true} (the default), tools will be automatically used/applied by the
+ * library. If set to {@code false}, tool calls will be returned to the client for manual
+ * handling.
+ *
+ *
Disabling this should be an explicit operation.
+ */
+ private boolean useTools = true;
+
+ public OllamaChatRequest() {}
public OllamaChatRequest(String model, boolean think, List messages) {
this.model = model;
@@ -42,5 +60,4 @@ public class OllamaChatRequest extends OllamaCommonRequest implements OllamaRequ
return this.toString().equals(o.toString());
}
-
}
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 4a9caf9..f72759f 100644
--- a/src/main/java/io/github/ollama4j/models/chat/OllamaChatRequestBuilder.java
+++ b/src/main/java/io/github/ollama4j/models/chat/OllamaChatRequestBuilder.java
@@ -1,38 +1,59 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.chat;
import io.github.ollama4j.utils.Options;
import io.github.ollama4j.utils.Utils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.io.File;
import java.io.IOException;
-import java.net.URISyntaxException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
+import lombok.Setter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-/**
- * Helper class for creating {@link OllamaChatRequest} objects using the builder-pattern.
- */
+/** Helper class for creating {@link OllamaChatRequest} objects using the builder-pattern. */
public class OllamaChatRequestBuilder {
private static final Logger LOG = LoggerFactory.getLogger(OllamaChatRequestBuilder.class);
- private OllamaChatRequestBuilder(String model, List messages) {
- request = new OllamaChatRequest(model, false, messages);
- }
-
+ private int imageURLConnectTimeoutSeconds = 10;
+ private int imageURLReadTimeoutSeconds = 10;
private OllamaChatRequest request;
+ @Setter private boolean useTools = true;
- public static OllamaChatRequestBuilder getInstance(String model) {
- return new OllamaChatRequestBuilder(model, new ArrayList<>());
+ private OllamaChatRequestBuilder() {
+ request = new OllamaChatRequest();
+ request.setMessages(new ArrayList<>());
}
- public OllamaChatRequest build() {
- return request;
+ public static OllamaChatRequestBuilder builder() {
+ return new OllamaChatRequestBuilder();
+ }
+
+ public OllamaChatRequestBuilder withImageURLConnectTimeoutSeconds(
+ int imageURLConnectTimeoutSeconds) {
+ this.imageURLConnectTimeoutSeconds = imageURLConnectTimeoutSeconds;
+ return this;
+ }
+
+ public OllamaChatRequestBuilder withImageURLReadTimeoutSeconds(int imageURLReadTimeoutSeconds) {
+ this.imageURLReadTimeoutSeconds = imageURLReadTimeoutSeconds;
+ return this;
+ }
+
+ public OllamaChatRequestBuilder withModel(String model) {
+ request.setModel(model);
+ return this;
}
public void reset() {
@@ -43,50 +64,79 @@ public class OllamaChatRequestBuilder {
return withMessage(role, content, Collections.emptyList());
}
- public OllamaChatRequestBuilder withMessage(OllamaChatMessageRole role, String content, List toolCalls) {
+ public OllamaChatRequestBuilder withMessage(
+ OllamaChatMessageRole role, String content, List toolCalls) {
List messages = this.request.getMessages();
messages.add(new OllamaChatMessage(role, content, null, toolCalls, null));
return this;
}
- public OllamaChatRequestBuilder withMessage(OllamaChatMessageRole role, String content, List toolCalls, List images) {
+ public OllamaChatRequestBuilder withMessage(
+ OllamaChatMessageRole role,
+ String content,
+ List toolCalls,
+ List images) {
List messages = this.request.getMessages();
-
- List binaryImages = images.stream().map(file -> {
- try {
- return Files.readAllBytes(file.toPath());
- } catch (IOException e) {
- LOG.warn("File '{}' could not be accessed, will not add to message!", file.toPath(), e);
- return new byte[0];
- }
- }).collect(Collectors.toList());
-
+ List binaryImages =
+ images.stream()
+ .map(
+ file -> {
+ try {
+ return Files.readAllBytes(file.toPath());
+ } catch (IOException e) {
+ LOG.warn(
+ "File '{}' could not be accessed, will not add to"
+ + " message!",
+ file.toPath(),
+ e);
+ return new byte[0];
+ }
+ })
+ .collect(Collectors.toList());
messages.add(new OllamaChatMessage(role, content, null, toolCalls, binaryImages));
return this;
}
- public OllamaChatRequestBuilder withMessage(OllamaChatMessageRole role, String content, List toolCalls, String... imageUrls) {
+ public OllamaChatRequestBuilder withMessage(
+ OllamaChatMessageRole role,
+ String content,
+ List toolCalls,
+ String... imageUrls)
+ throws IOException, InterruptedException {
List messages = this.request.getMessages();
List binaryImages = null;
if (imageUrls.length > 0) {
binaryImages = new ArrayList<>();
for (String imageUrl : imageUrls) {
try {
- binaryImages.add(Utils.loadImageBytesFromUrl(imageUrl));
- } catch (URISyntaxException e) {
- LOG.warn("URL '{}' could not be accessed, will not add to message!", imageUrl, e);
+ binaryImages.add(
+ Utils.loadImageBytesFromUrl(
+ imageUrl,
+ imageURLConnectTimeoutSeconds,
+ imageURLReadTimeoutSeconds));
+ } catch (InterruptedException e) {
+ LOG.error("Failed to load image from URL: '{}'. Cause: {}", imageUrl, e);
+ Thread.currentThread().interrupt();
+ throw new InterruptedException(
+ "Interrupted while loading image from URL: " + imageUrl);
} catch (IOException e) {
- LOG.warn("Content of URL '{}' could not be read, will not add to message!", imageUrl, e);
+ LOG.error(
+ "IOException occurred while loading image from URL '{}'. Cause: {}",
+ imageUrl,
+ e.getMessage(),
+ e);
+ throw new IOException(
+ "IOException while loading image from URL: " + imageUrl, e);
}
}
}
-
messages.add(new OllamaChatMessage(role, content, null, toolCalls, binaryImages));
return this;
}
public OllamaChatRequestBuilder withMessages(List messages) {
- return new OllamaChatRequestBuilder(request.getModel(), messages);
+ request.setMessages(messages);
+ return this;
}
public OllamaChatRequestBuilder withOptions(Options options) {
@@ -95,7 +145,7 @@ public class OllamaChatRequestBuilder {
}
public OllamaChatRequestBuilder withGetJsonResponse() {
- this.request.setReturnFormatJson(true);
+ this.request.setFormat("json");
return this;
}
@@ -118,4 +168,9 @@ public class OllamaChatRequestBuilder {
this.request.setThink(think);
return this;
}
+
+ public OllamaChatRequest build() {
+ request.setUseTools(useTools);
+ return request;
+ }
}
diff --git a/src/main/java/io/github/ollama4j/models/chat/OllamaChatResponseModel.java b/src/main/java/io/github/ollama4j/models/chat/OllamaChatResponseModel.java
index 2ccc731..5c05a94 100644
--- a/src/main/java/io/github/ollama4j/models/chat/OllamaChatResponseModel.java
+++ b/src/main/java/io/github/ollama4j/models/chat/OllamaChatResponseModel.java
@@ -1,18 +1,25 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.chat;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.List;
import lombok.Data;
-import java.util.List;
-
@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
public class OllamaChatResponseModel {
private String model;
private @JsonProperty("created_at") String createdAt;
private @JsonProperty("done_reason") String doneReason;
- private OllamaChatMessage message;
private boolean done;
- private String error;
private List context;
private @JsonProperty("total_duration") Long totalDuration;
private @JsonProperty("load_duration") Long loadDuration;
@@ -20,4 +27,6 @@ public class OllamaChatResponseModel {
private @JsonProperty("eval_duration") Long evalDuration;
private @JsonProperty("prompt_eval_count") Integer promptEvalCount;
private @JsonProperty("eval_count") Integer evalCount;
+ private String error;
+ private OllamaChatMessage message;
}
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 5fbf7e3..db0ddf2 100644
--- a/src/main/java/io/github/ollama4j/models/chat/OllamaChatResult.java
+++ b/src/main/java/io/github/ollama4j/models/chat/OllamaChatResult.java
@@ -1,12 +1,19 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.chat;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import lombok.Getter;
-
-import java.util.List;
-
import static io.github.ollama4j.utils.Utils.getObjectMapper;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import java.util.List;
+import lombok.Getter;
+
/**
* Specific chat-API result that contains the chat history sent to the model and appends the answer as {@link OllamaChatResult} given by the
* {@link OllamaChatMessageRole#ASSISTANT} role.
@@ -18,7 +25,8 @@ public class OllamaChatResult {
private final OllamaChatResponseModel responseModel;
- public OllamaChatResult(OllamaChatResponseModel responseModel, List chatHistory) {
+ public OllamaChatResult(
+ OllamaChatResponseModel responseModel, List chatHistory) {
this.chatHistory = chatHistory;
this.responseModel = responseModel;
appendAnswerToChatHistory(responseModel);
@@ -36,19 +44,4 @@ public class OllamaChatResult {
throw new RuntimeException(e);
}
}
-
- @Deprecated
- public String getResponse(){
- return responseModel != null ? responseModel.getMessage().getContent() : "";
- }
-
- @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 2ccdb74..776b006 100644
--- a/src/main/java/io/github/ollama4j/models/chat/OllamaChatStreamObserver.java
+++ b/src/main/java/io/github/ollama4j/models/chat/OllamaChatStreamObserver.java
@@ -1,15 +1,24 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.chat;
-import io.github.ollama4j.models.generate.OllamaStreamHandler;
-import io.github.ollama4j.models.generate.OllamaTokenHandler;
-import lombok.RequiredArgsConstructor;
+import io.github.ollama4j.models.generate.OllamaGenerateTokenHandler;
+import lombok.AllArgsConstructor;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
-@RequiredArgsConstructor
-public class OllamaChatStreamObserver implements OllamaTokenHandler {
- private final OllamaStreamHandler thinkingStreamHandler;
- private final OllamaStreamHandler responseStreamHandler;
-
- private String message = "";
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public class OllamaChatStreamObserver implements OllamaChatTokenHandler {
+ private OllamaGenerateTokenHandler thinkingStreamHandler;
+ private OllamaGenerateTokenHandler responseStreamHandler;
@Override
public void accept(OllamaChatResponseModel token) {
@@ -18,34 +27,15 @@ public class OllamaChatStreamObserver implements OllamaTokenHandler {
}
String thinking = token.getMessage().getThinking();
- String content = token.getMessage().getContent();
+ String response = token.getMessage().getResponse();
boolean hasThinking = thinking != null && !thinking.isEmpty();
- boolean hasContent = !content.isEmpty();
+ boolean hasResponse = response != null && !response.isEmpty();
-// if (hasThinking && !hasContent) {
-//// message += thinking;
-// message = thinking;
-// } else {
-//// message += content;
-// message = content;
-// }
-//
-// responseStreamHandler.accept(message);
-
-
- if (!hasContent && hasThinking && thinkingStreamHandler != null) {
- // message = message + thinking;
-
- // use only new tokens received, instead of appending the tokens to the previous
- // ones and sending the full string again
+ if (!hasResponse && hasThinking && thinkingStreamHandler != null) {
thinkingStreamHandler.accept(thinking);
- } else if (hasContent && responseStreamHandler != null) {
- // message = message + response;
-
- // use only new tokens received, instead of appending the tokens to the previous
- // ones and sending the full string again
- responseStreamHandler.accept(content);
+ } else if (hasResponse) {
+ responseStreamHandler.accept(response);
}
}
}
diff --git a/src/main/java/io/github/ollama4j/models/chat/OllamaChatTokenHandler.java b/src/main/java/io/github/ollama4j/models/chat/OllamaChatTokenHandler.java
new file mode 100644
index 0000000..fba39df
--- /dev/null
+++ b/src/main/java/io/github/ollama4j/models/chat/OllamaChatTokenHandler.java
@@ -0,0 +1,13 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
+package io.github.ollama4j.models.chat;
+
+import java.util.function.Consumer;
+
+public interface OllamaChatTokenHandler extends Consumer {}
diff --git a/src/main/java/io/github/ollama4j/models/chat/OllamaChatToolCalls.java b/src/main/java/io/github/ollama4j/models/chat/OllamaChatToolCalls.java
index de1a081..29faeb1 100644
--- a/src/main/java/io/github/ollama4j/models/chat/OllamaChatToolCalls.java
+++ b/src/main/java/io/github/ollama4j/models/chat/OllamaChatToolCalls.java
@@ -1,3 +1,11 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.chat;
import io.github.ollama4j.tools.OllamaToolCallsFunction;
@@ -11,6 +19,4 @@ import lombok.NoArgsConstructor;
public class OllamaChatToolCalls {
private OllamaToolCallsFunction function;
-
-
}
diff --git a/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbedRequestModel.java b/src/main/java/io/github/ollama4j/models/embed/OllamaEmbedRequest.java
similarity index 61%
rename from src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbedRequestModel.java
rename to src/main/java/io/github/ollama4j/models/embed/OllamaEmbedRequest.java
index 8cb2002..8c2fae8 100644
--- a/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbedRequestModel.java
+++ b/src/main/java/io/github/ollama4j/models/embed/OllamaEmbedRequest.java
@@ -1,26 +1,29 @@
-package io.github.ollama4j.models.embeddings;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.NonNull;
-import lombok.RequiredArgsConstructor;
-
-import java.util.List;
-import java.util.Map;
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
+package io.github.ollama4j.models.embed;
import static io.github.ollama4j.utils.Utils.getObjectMapper;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import java.util.List;
+import java.util.Map;
+import lombok.*;
+
+@SuppressWarnings("NullableProblems")
@Data
@RequiredArgsConstructor
@NoArgsConstructor
-public class OllamaEmbedRequestModel {
- @NonNull
- private String model;
+public class OllamaEmbedRequest {
+ @NonNull private String model;
- @NonNull
- private List input;
+ @NonNull private List input;
private Map options;
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 60%
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 83c619d..8e551ca 100644
--- a/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbedRequestBuilder.java
+++ b/src/main/java/io/github/ollama4j/models/embed/OllamaEmbedRequestBuilder.java
@@ -1,7 +1,14 @@
-package io.github.ollama4j.models.embeddings;
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
+package io.github.ollama4j.models.embed;
import io.github.ollama4j.utils.Options;
-
import java.util.List;
/**
@@ -9,32 +16,32 @@ import java.util.List;
*/
public class OllamaEmbedRequestBuilder {
- private final OllamaEmbedRequestModel request;
+ private final OllamaEmbedRequest request;
private OllamaEmbedRequestBuilder(String model, List input) {
- this.request = new OllamaEmbedRequestModel(model,input);
+ this.request = new OllamaEmbedRequest(model, input);
}
- public static OllamaEmbedRequestBuilder getInstance(String model, String... input){
+ public static OllamaEmbedRequestBuilder getInstance(String model, String... input) {
return new OllamaEmbedRequestBuilder(model, List.of(input));
}
- public OllamaEmbedRequestBuilder withOptions(Options options){
+ public OllamaEmbedRequestBuilder withOptions(Options options) {
this.request.setOptions(options.getOptionsMap());
return this;
}
- public OllamaEmbedRequestBuilder withKeepAlive(String keepAlive){
+ public OllamaEmbedRequestBuilder withKeepAlive(String keepAlive) {
this.request.setKeepAlive(keepAlive);
return this;
}
- public OllamaEmbedRequestBuilder withoutTruncate(){
+ public OllamaEmbedRequestBuilder withoutTruncate() {
this.request.setTruncate(false);
return this;
}
- public OllamaEmbedRequestModel build() {
+ public OllamaEmbedRequest build() {
return this.request;
}
}
diff --git a/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbedResponseModel.java b/src/main/java/io/github/ollama4j/models/embed/OllamaEmbedResult.java
similarity index 59%
rename from src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbedResponseModel.java
rename to src/main/java/io/github/ollama4j/models/embed/OllamaEmbedResult.java
index b4f808c..512872d 100644
--- a/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbedResponseModel.java
+++ b/src/main/java/io/github/ollama4j/models/embed/OllamaEmbedResult.java
@@ -1,13 +1,20 @@
-package io.github.ollama4j.models.embeddings;
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
+package io.github.ollama4j.models.embed;
import com.fasterxml.jackson.annotation.JsonProperty;
-import lombok.Data;
-
import java.util.List;
+import lombok.Data;
@SuppressWarnings("unused")
@Data
-public class OllamaEmbedResponseModel {
+public class OllamaEmbedResult {
@JsonProperty("model")
private String model;
diff --git a/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbeddingResponseModel.java b/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbeddingResponseModel.java
deleted file mode 100644
index 2d0d90a..0000000
--- a/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbeddingResponseModel.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package io.github.ollama4j.models.embeddings;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import lombok.Data;
-
-import java.util.List;
-
-@SuppressWarnings("unused")
-@Data
-@Deprecated(since="1.0.90")
-public class OllamaEmbeddingResponseModel {
- @JsonProperty("embedding")
- private List embedding;
-}
diff --git a/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbeddingsRequestBuilder.java b/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbeddingsRequestBuilder.java
deleted file mode 100644
index 47daf75..0000000
--- a/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbeddingsRequestBuilder.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package io.github.ollama4j.models.embeddings;
-
-import io.github.ollama4j.utils.Options;
-
-@Deprecated(since="1.0.90")
-public class OllamaEmbeddingsRequestBuilder {
-
- private OllamaEmbeddingsRequestBuilder(String model, String prompt){
- request = new OllamaEmbeddingsRequestModel(model, prompt);
- }
-
- private OllamaEmbeddingsRequestModel request;
-
- public static OllamaEmbeddingsRequestBuilder getInstance(String model, String prompt){
- return new OllamaEmbeddingsRequestBuilder(model, prompt);
- }
-
- public OllamaEmbeddingsRequestModel build(){
- return request;
- }
-
- public OllamaEmbeddingsRequestBuilder withOptions(Options options){
- this.request.setOptions(options.getOptionsMap());
- return this;
- }
-
- public OllamaEmbeddingsRequestBuilder withKeepAlive(String keepAlive){
- this.request.setKeepAlive(keepAlive);
- return this;
- }
-
-}
diff --git a/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbeddingsRequestModel.java b/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbeddingsRequestModel.java
deleted file mode 100644
index 7d113f0..0000000
--- a/src/main/java/io/github/ollama4j/models/embeddings/OllamaEmbeddingsRequestModel.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package io.github.ollama4j.models.embeddings;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.NonNull;
-import lombok.RequiredArgsConstructor;
-
-import java.util.Map;
-
-import static io.github.ollama4j.utils.Utils.getObjectMapper;
-
-@Data
-@RequiredArgsConstructor
-@NoArgsConstructor
-@Deprecated(since="1.0.90")
-public class OllamaEmbeddingsRequestModel {
- @NonNull
- private String model;
- @NonNull
- private String prompt;
-
- protected Map options;
- @JsonProperty(value = "keep_alive")
- private String keepAlive;
-
- @Override
- public String toString() {
- try {
- return getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this);
- } catch (JsonProcessingException e) {
- throw new RuntimeException(e);
- }
- }
-}
diff --git a/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateRequest.java b/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateRequest.java
index 3763f0a..05ad9c8 100644
--- a/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateRequest.java
+++ b/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateRequest.java
@@ -1,46 +1,51 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.generate;
-
import io.github.ollama4j.models.request.OllamaCommonRequest;
+import io.github.ollama4j.tools.Tools;
import io.github.ollama4j.utils.OllamaRequestBody;
+import java.util.List;
import lombok.Getter;
import lombok.Setter;
-import java.util.List;
-
@Getter
@Setter
-public class OllamaGenerateRequest extends OllamaCommonRequest implements OllamaRequestBody{
+public class OllamaGenerateRequest extends OllamaCommonRequest implements OllamaRequestBody {
- private String prompt;
- private List images;
+ private String prompt;
+ private List images;
+ private String system;
+ private String context;
+ private boolean raw;
+ private boolean think;
+ private boolean useTools;
+ private List tools;
- private String system;
- private String context;
- private boolean raw;
- private boolean think;
+ public OllamaGenerateRequest() {}
- public OllamaGenerateRequest() {
- }
-
- public OllamaGenerateRequest(String model, String prompt) {
- this.model = model;
- this.prompt = prompt;
- }
-
- public OllamaGenerateRequest(String model, String prompt, List images) {
- this.model = model;
- this.prompt = prompt;
- this.images = images;
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof OllamaGenerateRequest)) {
- return false;
+ public OllamaGenerateRequest(String model, String prompt) {
+ this.model = model;
+ this.prompt = prompt;
}
- return this.toString().equals(o.toString());
- }
+ public OllamaGenerateRequest(String model, String prompt, List images) {
+ this.model = model;
+ this.prompt = prompt;
+ this.images = images;
+ }
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof OllamaGenerateRequest)) {
+ return false;
+ }
+ return this.toString().equals(o.toString());
+ }
}
diff --git a/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateRequestBuilder.java b/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateRequestBuilder.java
index 713c46e..0717f9e 100644
--- a/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateRequestBuilder.java
+++ b/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateRequestBuilder.java
@@ -1,55 +1,121 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.generate;
+import io.github.ollama4j.tools.Tools;
import io.github.ollama4j.utils.Options;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
-/**
- * Helper class for creating {@link OllamaGenerateRequest}
- * objects using the builder-pattern.
- */
+/** Helper class for creating {@link OllamaGenerateRequest} objects using the builder-pattern. */
public class OllamaGenerateRequestBuilder {
- private OllamaGenerateRequestBuilder(String model, String prompt){
- request = new OllamaGenerateRequest(model, prompt);
+ private OllamaGenerateRequestBuilder() {
+ request = new OllamaGenerateRequest();
}
private OllamaGenerateRequest request;
- public static OllamaGenerateRequestBuilder getInstance(String model){
- return new OllamaGenerateRequestBuilder(model,"");
+ public static OllamaGenerateRequestBuilder builder() {
+ return new OllamaGenerateRequestBuilder();
}
- public OllamaGenerateRequest build(){
+ public OllamaGenerateRequest build() {
return request;
}
- public OllamaGenerateRequestBuilder withPrompt(String prompt){
+ public OllamaGenerateRequestBuilder withPrompt(String prompt) {
request.setPrompt(prompt);
return this;
}
- public OllamaGenerateRequestBuilder withGetJsonResponse(){
- this.request.setReturnFormatJson(true);
+ public OllamaGenerateRequestBuilder withTools(List tools) {
+ request.setTools(tools);
return this;
}
- public OllamaGenerateRequestBuilder withOptions(Options options){
+ public OllamaGenerateRequestBuilder withModel(String model) {
+ request.setModel(model);
+ return this;
+ }
+
+ public OllamaGenerateRequestBuilder withGetJsonResponse() {
+ this.request.setFormat("json");
+ return this;
+ }
+
+ public OllamaGenerateRequestBuilder withOptions(Options options) {
this.request.setOptions(options.getOptionsMap());
return this;
}
- public OllamaGenerateRequestBuilder withTemplate(String template){
+ public OllamaGenerateRequestBuilder withTemplate(String template) {
this.request.setTemplate(template);
return this;
}
- public OllamaGenerateRequestBuilder withStreaming(){
- this.request.setStream(true);
+ public OllamaGenerateRequestBuilder withStreaming(boolean streaming) {
+ this.request.setStream(streaming);
return this;
}
- public OllamaGenerateRequestBuilder withKeepAlive(String keepAlive){
+ public OllamaGenerateRequestBuilder withKeepAlive(String keepAlive) {
this.request.setKeepAlive(keepAlive);
return this;
}
+ public OllamaGenerateRequestBuilder withRaw(boolean raw) {
+ this.request.setRaw(raw);
+ return this;
+ }
+
+ public OllamaGenerateRequestBuilder withThink(boolean think) {
+ this.request.setThink(think);
+ return this;
+ }
+
+ public OllamaGenerateRequestBuilder withUseTools(boolean useTools) {
+ this.request.setUseTools(useTools);
+ return this;
+ }
+
+ public OllamaGenerateRequestBuilder withFormat(java.util.Map format) {
+ this.request.setFormat(format);
+ return this;
+ }
+
+ public OllamaGenerateRequestBuilder withSystem(String system) {
+ this.request.setSystem(system);
+ return this;
+ }
+
+ public OllamaGenerateRequestBuilder withContext(String context) {
+ this.request.setContext(context);
+ return this;
+ }
+
+ public OllamaGenerateRequestBuilder withImagesBase64(java.util.List images) {
+ this.request.setImages(images);
+ return this;
+ }
+
+ public OllamaGenerateRequestBuilder withImages(java.util.List imageFiles)
+ throws IOException {
+ java.util.List images = new ArrayList<>();
+ for (File imageFile : imageFiles) {
+ images.add(Base64.getEncoder().encodeToString(Files.readAllBytes(imageFile.toPath())));
+ }
+ this.request.setImages(images);
+ return this;
+ }
}
diff --git a/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateResponseModel.java b/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateResponseModel.java
index a3d23ec..bf33133 100644
--- a/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateResponseModel.java
+++ b/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateResponseModel.java
@@ -1,25 +1,32 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.generate;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
-import lombok.Data;
-
import java.util.List;
+import lombok.Data;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class OllamaGenerateResponseModel {
private String model;
private @JsonProperty("created_at") String createdAt;
- private String response;
- private String thinking;
- private boolean done;
private @JsonProperty("done_reason") String doneReason;
+ private boolean done;
private List context;
private @JsonProperty("total_duration") Long totalDuration;
private @JsonProperty("load_duration") Long loadDuration;
- private @JsonProperty("prompt_eval_count") Integer promptEvalCount;
private @JsonProperty("prompt_eval_duration") Long promptEvalDuration;
- private @JsonProperty("eval_count") Integer evalCount;
private @JsonProperty("eval_duration") Long evalDuration;
+ private @JsonProperty("prompt_eval_count") Integer promptEvalCount;
+ private @JsonProperty("eval_count") Integer evalCount;
+ private String response;
+ private String thinking;
}
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 67ae571..0e908dc 100644
--- a/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateStreamObserver.java
+++ b/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateStreamObserver.java
@@ -1,20 +1,29 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.generate;
import java.util.ArrayList;
import java.util.List;
+import lombok.Getter;
+@Getter
public class OllamaGenerateStreamObserver {
-
- private final OllamaStreamHandler thinkingStreamHandler;
- private final OllamaStreamHandler responseStreamHandler;
+ private final OllamaGenerateTokenHandler thinkingStreamHandler;
+ private final OllamaGenerateTokenHandler responseStreamHandler;
private final List responseParts = new ArrayList<>();
- private String message = "";
-
- public OllamaGenerateStreamObserver(OllamaStreamHandler thinkingStreamHandler, OllamaStreamHandler responseStreamHandler) {
- this.responseStreamHandler = responseStreamHandler;
+ public OllamaGenerateStreamObserver(
+ OllamaGenerateTokenHandler thinkingStreamHandler,
+ OllamaGenerateTokenHandler responseStreamHandler) {
this.thinkingStreamHandler = thinkingStreamHandler;
+ this.responseStreamHandler = responseStreamHandler;
}
public void notify(OllamaGenerateResponseModel currentResponsePart) {
@@ -30,16 +39,8 @@ public class OllamaGenerateStreamObserver {
boolean hasThinking = thinking != null && !thinking.isEmpty();
if (!hasResponse && hasThinking && thinkingStreamHandler != null) {
- // message = message + thinking;
-
- // 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) {
- // message = message + response;
-
- // 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/generate/OllamaGenerateTokenHandler.java b/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateTokenHandler.java
new file mode 100644
index 0000000..d8d9d01
--- /dev/null
+++ b/src/main/java/io/github/ollama4j/models/generate/OllamaGenerateTokenHandler.java
@@ -0,0 +1,15 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
+package io.github.ollama4j.models.generate;
+
+import java.util.function.Consumer;
+
+public interface OllamaGenerateTokenHandler extends Consumer {
+ void accept(String message);
+}
diff --git a/src/main/java/io/github/ollama4j/models/generate/OllamaStreamHandler.java b/src/main/java/io/github/ollama4j/models/generate/OllamaStreamHandler.java
deleted file mode 100644
index e2da640..0000000
--- a/src/main/java/io/github/ollama4j/models/generate/OllamaStreamHandler.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package io.github.ollama4j.models.generate;
-
-import java.util.function.Consumer;
-
-public interface OllamaStreamHandler extends Consumer {
- void accept(String message);
-}
diff --git a/src/main/java/io/github/ollama4j/models/generate/OllamaTokenHandler.java b/src/main/java/io/github/ollama4j/models/generate/OllamaTokenHandler.java
deleted file mode 100644
index a0aed8c..0000000
--- a/src/main/java/io/github/ollama4j/models/generate/OllamaTokenHandler.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package io.github.ollama4j.models.generate;
-
-import io.github.ollama4j.models.chat.OllamaChatResponseModel;
-
-import java.util.function.Consumer;
-
-public interface OllamaTokenHandler extends Consumer {
-}
diff --git a/src/main/java/io/github/ollama4j/models/ps/ModelsProcessResponse.java b/src/main/java/io/github/ollama4j/models/ps/ModelProcessesResult.java
similarity index 79%
rename from src/main/java/io/github/ollama4j/models/ps/ModelsProcessResponse.java
rename to src/main/java/io/github/ollama4j/models/ps/ModelProcessesResult.java
index 490d362..257d019 100644
--- a/src/main/java/io/github/ollama4j/models/ps/ModelsProcessResponse.java
+++ b/src/main/java/io/github/ollama4j/models/ps/ModelProcessesResult.java
@@ -1,21 +1,29 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.ps;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.List;
import lombok.Data;
import lombok.NoArgsConstructor;
-import java.util.List;
-
@Data
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
-public class ModelsProcessResponse {
+public class ModelProcessesResult {
@JsonProperty("models")
private List models;
@Data
@NoArgsConstructor
+ @JsonIgnoreProperties(ignoreUnknown = true)
public static class ModelProcess {
@JsonProperty("name")
private String name;
@@ -33,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/Auth.java b/src/main/java/io/github/ollama4j/models/request/Auth.java
index 70c9c1b..d81e817 100644
--- a/src/main/java/io/github/ollama4j/models/request/Auth.java
+++ b/src/main/java/io/github/ollama4j/models/request/Auth.java
@@ -1,10 +1,18 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.request;
public abstract class Auth {
- /**
- * Get authentication header value.
- *
- * @return authentication header value
- */
- public abstract String getAuthHeaderValue();
+ /**
+ * Get authentication header value.
+ *
+ * @return authentication header value
+ */
+ public abstract String getAuthHeaderValue();
}
diff --git a/src/main/java/io/github/ollama4j/models/request/BasicAuth.java b/src/main/java/io/github/ollama4j/models/request/BasicAuth.java
index 13f6a59..80e6653 100644
--- a/src/main/java/io/github/ollama4j/models/request/BasicAuth.java
+++ b/src/main/java/io/github/ollama4j/models/request/BasicAuth.java
@@ -1,25 +1,32 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.request;
+import java.util.Base64;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
-import java.util.Base64;
-
@Data
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class BasicAuth extends Auth {
- private String username;
- private String password;
+ 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());
- }
+ /**
+ * 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());
+ }
}
diff --git a/src/main/java/io/github/ollama4j/models/request/BearerAuth.java b/src/main/java/io/github/ollama4j/models/request/BearerAuth.java
index 4d876f2..cc25309 100644
--- a/src/main/java/io/github/ollama4j/models/request/BearerAuth.java
+++ b/src/main/java/io/github/ollama4j/models/request/BearerAuth.java
@@ -1,3 +1,11 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.request;
import lombok.AllArgsConstructor;
diff --git a/src/main/java/io/github/ollama4j/models/request/CustomModelFileContentsRequest.java b/src/main/java/io/github/ollama4j/models/request/CustomModelFileContentsRequest.java
index 52bc684..b01e18c 100644
--- a/src/main/java/io/github/ollama4j/models/request/CustomModelFileContentsRequest.java
+++ b/src/main/java/io/github/ollama4j/models/request/CustomModelFileContentsRequest.java
@@ -1,23 +1,32 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.request;
+import static io.github.ollama4j.utils.Utils.getObjectMapper;
+
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.AllArgsConstructor;
import lombok.Data;
-import static io.github.ollama4j.utils.Utils.getObjectMapper;
-
+@SuppressWarnings("SpellCheckingInspection")
@Data
@AllArgsConstructor
public class CustomModelFileContentsRequest {
- private String name;
- private String modelfile;
+ private String name;
+ private String modelfile;
- @Override
- public String toString() {
- try {
- return getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this);
- } catch (JsonProcessingException e) {
- throw new RuntimeException(e);
+ @Override
+ public String toString() {
+ try {
+ return getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
}
- }
}
diff --git a/src/main/java/io/github/ollama4j/models/request/CustomModelFilePathRequest.java b/src/main/java/io/github/ollama4j/models/request/CustomModelFilePathRequest.java
index 578e1c0..9ac9eb4 100644
--- a/src/main/java/io/github/ollama4j/models/request/CustomModelFilePathRequest.java
+++ b/src/main/java/io/github/ollama4j/models/request/CustomModelFilePathRequest.java
@@ -1,23 +1,31 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.request;
+import static io.github.ollama4j.utils.Utils.getObjectMapper;
+
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.AllArgsConstructor;
import lombok.Data;
-import static io.github.ollama4j.utils.Utils.getObjectMapper;
-
@Data
@AllArgsConstructor
public class CustomModelFilePathRequest {
- private String name;
- private String path;
+ private String name;
+ private String path;
- @Override
- public String toString() {
- try {
- return getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this);
- } catch (JsonProcessingException e) {
- throw new RuntimeException(e);
+ @Override
+ public String toString() {
+ try {
+ return getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
}
- }
}
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 b2ecb91..7cd7417 100644
--- a/src/main/java/io/github/ollama4j/models/request/CustomModelRequest.java
+++ b/src/main/java/io/github/ollama4j/models/request/CustomModelRequest.java
@@ -1,15 +1,21 @@
+/*
+ * Ollama4j - Java library for interacting with Ollama server.
+ * Copyright (c) 2025 Amith Koujalgi and contributors.
+ *
+ * Licensed under the MIT License (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+*/
package io.github.ollama4j.models.request;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-
-import java.util.List;
-import java.util.Map;
-
import static io.github.ollama4j.utils.Utils.getObjectMapper;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import java.util.List;
+import java.util.Map;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
@Data
@AllArgsConstructor
@@ -20,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