Merge pull request #183 from ollama4j/refactor
Some checks failed
Mark stale issues / stale (push) Failing after 21s
Mark stale issues and PRs / stale (push) Failing after 1m33s
CodeQL / Analyze (javascript) (push) Failing after 11s
CodeQL / Analyze (java) (push) Failing after 13s

Refactor: with breaking changes
This commit is contained in:
Amith Koujalgi 2025-09-28 23:11:19 +05:30 committed by GitHub
commit 6fce6ec777
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
135 changed files with 5115 additions and 3684 deletions

View File

@ -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 }}

View File

@ -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

View File

@ -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 }}

View File

@ -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

View File

@ -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

View File

@ -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 }}

View File

@ -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

View File

@ -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

View File

@ -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
@echo "\033[0;34mStarting Ollama (GPU mode)...\033[0m"
@docker run -it --gpus=all -v ~/ollama:/root/.ollama -p 11434:11434 ollama/ollama

View File

@ -1,7 +1,8 @@
<div align="center">
<img src='https://raw.githubusercontent.com/ollama4j/ollama4j/65a9d526150da8fcd98e2af6a164f055572bf722/ollama4j.jpeg' width='100' alt="ollama4j-icon">
### Ollama4j
### Ollama4j
</div>
<div align="center">
@ -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.
<div align="center">
<img src='metrics.png' width='100%' alt="ollama4j-icon">
</div>
## How does it work?
@ -73,7 +111,7 @@ _Find more details on the **[website](https://ollama4j.github.io/ollama4j/)**._
</a>
</p>
## 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/).

184
docs/METRICS.md Normal file
View File

@ -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<String, Object> 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();
```

View File

@ -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");

View File

@ -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.

View File

@ -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.

View File

@ -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
<configuration>
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
</root>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
</configuration>
```

View File

@ -1,5 +1,5 @@
---
sidebar_position: 3
sidebar_position: 5
---
# Ping

View File

@ -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
@ -72,3 +73,4 @@ func readFile(fileName string) {
}
}
```
:::

View File

@ -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);
}

View File

@ -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.

View File

@ -21,25 +21,25 @@ session. The tool invocation and response handling are all managed internally by
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/ChatWithTools.java"/>
::::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 multistep orchestration).
To enable this behavior, set the clientHandlesTools flag to true on your OllamaAPI instance. When enabled, ollama4j will stop autoexecuting 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 autoexecuting 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 autoexecutes 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 recalling chat() to continue.
- Default value: useTools is true.
- When useTools is false, ollama4j autoexecutes 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 recalling 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
::::
:::

View File

@ -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.
::::
:::

View File

@ -1,5 +1,5 @@
---
sidebar_position: 2
sidebar_position: 6
---
import CodeEmbed from '@site/src/components/CodeEmbed';

View File

@ -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.
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/GenerateEmbeddings.java" />
::::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:
]
```
::::
:::

View File

@ -1,5 +1,5 @@
---
sidebar_position: 2
sidebar_position: 3
---
import CodeEmbed from '@site/src/components/CodeEmbed';

View File

@ -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:
![Img](https://t3.ftcdn.net/jpg/02/96/63/80/360_F_296638053_0gUVA4WVBKceGsIr7LNqRWSnkusi07dq.jpg)
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/GenerateWithImageURL.java" />
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.
::::

View File

@ -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.
::::
:::
# 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:
![Img](https://t3.ftcdn.net/jpg/02/96/63/80/360_F_296638053_0gUVA4WVBKceGsIr7LNqRWSnkusi07dq.jpg)
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/GenerateWithImageURL.java" />
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.
:::

View File

@ -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}`
::::
:::

View File

@ -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
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/GenerateStructuredOutputMappedToObject.java" />
::::tip[LLM Response]
:::tip[LLM Response]
HeroInfo(heroName=Batman, ageOfPerson=30)
::::
:::

View File

@ -1,5 +1,5 @@
{
"label": "APIs - Model Management",
"label": "APIs - Manage Models",
"position": 2,
"link": {
"type": "generated-index",

BIN
metrics.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 KiB

77
pom.xml
View File

@ -163,6 +163,69 @@
<dateFormatTimeZone>Etc/UTC</dateFormatTimeZone>
</configuration>
</plugin>
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<version>2.46.1</version>
<configuration>
<formats>
<!-- you can define as many formats as you want, each is independent -->
<format>
<!-- define the files to apply to -->
<includes>
<include>.gitattributes</include>
<include>.gitignore</include>
</includes>
<!-- define the steps to apply to those files -->
<trimTrailingWhitespace/>
<endWithNewline/>
<indent>
<tabs>true</tabs>
<spacesPerTab>4</spacesPerTab>
</indent>
</format>
</formats>
<!-- define a language-specific format -->
<java>
<!-- no need to specify files, inferred automatically, but you can if you want -->
<!-- apply a specific flavor of google-java-format and reflow long strings -->
<googleJavaFormat>
<version>1.28.0</version>
<style>AOSP</style>
<reflowLongStrings>true</reflowLongStrings>
<formatJavadoc>false</formatJavadoc>
</googleJavaFormat>
<!-- make sure every file has the following copyright header.
optionally, Spotless can set copyright years by digging
through git history (see "license" section below) -->
<licenseHeader>
<content>
<![CDATA[
/*
* Ollama4j - Java library for interacting with Ollama server.
* Copyright (c) $YEAR Amith Koujalgi and contributors.
*
* Licensed under the MIT License (the "License");
* you may not use this file except in compliance with the License.
*
*/
]]>
</content> <!-- or <file>${project.basedir}/license-header</file> -->
</licenseHeader>
</java>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
@ -212,6 +275,7 @@
<artifactId>slf4j-api</artifactId>
<version>2.0.17</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
@ -243,6 +307,19 @@
<version>1.21.3</version>
<scope>test</scope>
</dependency>
<!-- Prometheus metrics dependencies -->
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient</artifactId>
<version>0.16.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.5.0-jre</version>
</dependency>
</dependencies>
<distributionManagement>

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +0,0 @@
package io.github.ollama4j.exceptions;
public class OllamaBaseException extends Exception {
public OllamaBaseException(String s) {
super(s);
}
}

View File

@ -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);
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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<String, Object> 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<String, Object>) 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<String, Object> 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;
}
}

View File

@ -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 <a href="https://github.com/ollama/ollama/blob/main/docs/api.md#generate-a-chat-completion">Generate chat completion</a>
* @see <a
* href="https://github.com/ollama/ollama/blob/main/docs/api.md#generate-a-chat-completion">Generate
* chat completion</a>
*/
@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;

View File

@ -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);
}

View File

@ -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 <a href=
* "https://github.com/ollama/ollama/blob/main/docs/api.md#generate-a-chat-completion">Generate
* Chat Completion</a>
* "https://github.com/ollama/ollama/blob/main/docs/api.md#generate-a-chat-completion">Generate
* Chat Completion</a>
*/
@Getter
@Setter
public class OllamaChatRequest extends OllamaCommonRequest implements OllamaRequestBody {
private List<OllamaChatMessage> messages;
private List<OllamaChatMessage> messages = Collections.emptyList();
private List<Tools.PromptFuncDefinition> tools;
private List<Tools.Tool> tools;
private boolean think;
public OllamaChatRequest() {
}
/**
* Controls whether tools are automatically executed.
*
* <p>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.
*
* <p>Disabling this should be an explicit operation.
*/
private boolean useTools = true;
public OllamaChatRequest() {}
public OllamaChatRequest(String model, boolean think, List<OllamaChatMessage> messages) {
this.model = model;
@ -42,5 +60,4 @@ public class OllamaChatRequest extends OllamaCommonRequest implements OllamaRequ
return this.toString().equals(o.toString());
}
}

View File

@ -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<OllamaChatMessage> 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<OllamaChatToolCalls> toolCalls) {
public OllamaChatRequestBuilder withMessage(
OllamaChatMessageRole role, String content, List<OllamaChatToolCalls> toolCalls) {
List<OllamaChatMessage> messages = this.request.getMessages();
messages.add(new OllamaChatMessage(role, content, null, toolCalls, null));
return this;
}
public OllamaChatRequestBuilder withMessage(OllamaChatMessageRole role, String content, List<OllamaChatToolCalls> toolCalls, List<File> images) {
public OllamaChatRequestBuilder withMessage(
OllamaChatMessageRole role,
String content,
List<OllamaChatToolCalls> toolCalls,
List<File> images) {
List<OllamaChatMessage> messages = this.request.getMessages();
List<byte[]> 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<byte[]> 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<OllamaChatToolCalls> toolCalls, String... imageUrls) {
public OllamaChatRequestBuilder withMessage(
OllamaChatMessageRole role,
String content,
List<OllamaChatToolCalls> toolCalls,
String... imageUrls)
throws IOException, InterruptedException {
List<OllamaChatMessage> messages = this.request.getMessages();
List<byte[]> 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<OllamaChatMessage> 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;
}
}

View File

@ -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<Integer> 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;
}

View File

@ -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<OllamaChatMessage> chatHistory) {
public OllamaChatResult(
OllamaChatResponseModel responseModel, List<OllamaChatMessage> 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;
}
}

View File

@ -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);
}
}
}

View File

@ -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<OllamaChatResponseModel> {}

View File

@ -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;
}

View File

@ -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<String> input;
@NonNull private List<String> input;
private Map<String, Object> options;

View File

@ -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<String> 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;
}
}

View File

@ -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;

View File

@ -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<Double> embedding;
}

View File

@ -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;
}
}

View File

@ -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<String, Object> 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);
}
}
}

View File

@ -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<String> images;
private String prompt;
private List<String> images;
private String system;
private String context;
private boolean raw;
private boolean think;
private boolean useTools;
private List<Tools.Tool> 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<String> 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<String> 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());
}
}

View File

@ -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.Tool> 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<String, Object> 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<String> images) {
this.request.setImages(images);
return this;
}
public OllamaGenerateRequestBuilder withImages(java.util.List<File> imageFiles)
throws IOException {
java.util.List<String> images = new ArrayList<>();
for (File imageFile : imageFiles) {
images.add(Base64.getEncoder().encodeToString(Files.readAllBytes(imageFile.toPath())));
}
this.request.setImages(images);
return this;
}
}

View File

@ -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<Integer> 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;
}

View File

@ -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<OllamaGenerateResponseModel> 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);
}
}

View File

@ -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<String> {
void accept(String message);
}

View File

@ -1,7 +0,0 @@
package io.github.ollama4j.models.generate;
import java.util.function.Consumer;
public interface OllamaStreamHandler extends Consumer<String> {
void accept(String message);
}

View File

@ -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<OllamaChatResponseModel> {
}

View File

@ -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<ModelProcess> 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;

View File

@ -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();
}

View File

@ -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());
}
}

View File

@ -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;

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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<String, String> files;
private Map<String, String> adapters;
private String template;
private Object license; // Using Object to handle both String and List<String>
private Object license;
private String system;
private Map<String, Object> parameters;
private List<Object> messages;

View File

@ -1,22 +1,30 @@
/*
* 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 ModelRequest {
private String name;
private String name;
@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);
}
}
}
}

View File

@ -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 com.fasterxml.jackson.core.type.TypeReference;
import io.github.ollama4j.exceptions.OllamaBaseException;
import io.github.ollama4j.exceptions.OllamaException;
import io.github.ollama4j.metrics.MetricsRecorder;
import io.github.ollama4j.models.chat.*;
import io.github.ollama4j.models.generate.OllamaTokenHandler;
import io.github.ollama4j.models.chat.OllamaChatTokenHandler;
import io.github.ollama4j.models.response.OllamaErrorResponse;
import io.github.ollama4j.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
@ -20,50 +26,49 @@ import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Specialization class for requests
*/
/** Specialization class for requests */
@SuppressWarnings("resource")
public class OllamaChatEndpointCaller extends OllamaEndpointCaller {
private static final Logger LOG = LoggerFactory.getLogger(OllamaChatEndpointCaller.class);
public static final String endpoint = "/api/chat";
private OllamaTokenHandler tokenHandler;
private OllamaChatTokenHandler tokenHandler;
public OllamaChatEndpointCaller(String host, Auth auth, long requestTimeoutSeconds) {
super(host, auth, requestTimeoutSeconds);
}
@Override
protected String getEndpointSuffix() {
return "/api/chat";
}
/**
* Parses streamed Response line from ollama chat.
* Using {@link com.fasterxml.jackson.databind.ObjectMapper#readValue(String, TypeReference)} should throw
* {@link IllegalArgumentException} in case of null line or {@link com.fasterxml.jackson.core.JsonParseException}
* in case the JSON Object cannot be parsed to a {@link OllamaChatResponseModel}. Thus, the ResponseModel should
* never be null.
* Parses streamed Response line from ollama chat. Using {@link
* com.fasterxml.jackson.databind.ObjectMapper#readValue(String, TypeReference)} should throw
* {@link IllegalArgumentException} in case of null line or {@link
* com.fasterxml.jackson.core.JsonParseException} in case the JSON Object cannot be parsed to a
* {@link OllamaChatResponseModel}. Thus, the ResponseModel should never be null.
*
* @param line streamed line of ollama stream response
* @param line streamed line of ollama stream response
* @param responseBuffer Stringbuffer to add latest response message part to
* @return TRUE, if ollama-Response has 'done' state
*/
@Override
protected boolean parseResponseAndAddToBuffer(String line, StringBuilder responseBuffer, StringBuilder thinkingBuffer) {
protected boolean parseResponseAndAddToBuffer(
String line, StringBuilder responseBuffer, StringBuilder thinkingBuffer) {
try {
OllamaChatResponseModel ollamaResponseModel = Utils.getObjectMapper().readValue(line, OllamaChatResponseModel.class);
// it seems that under heavy load ollama responds with an empty chat message part in the streamed response
// thus, we null check the message and hope that the next streamed response has some message content again
OllamaChatResponseModel ollamaResponseModel =
Utils.getObjectMapper().readValue(line, OllamaChatResponseModel.class);
// It seems that under heavy load Ollama responds with an empty chat message part in the
// streamed response.
// Thus, we null check the message and hope that the next streamed response has some
// message content again.
OllamaChatMessage message = ollamaResponseModel.getMessage();
if (message != null) {
if (message.getThinking() != null) {
thinkingBuffer.append(message.getThinking());
}
else {
responseBuffer.append(message.getContent());
} else {
responseBuffer.append(message.getResponse());
}
if (tokenHandler != null) {
tokenHandler.accept(ollamaResponseModel);
@ -76,22 +81,22 @@ public class OllamaChatEndpointCaller extends OllamaEndpointCaller {
}
}
public OllamaChatResult call(OllamaChatRequest body, OllamaTokenHandler tokenHandler)
throws OllamaBaseException, IOException, InterruptedException {
public OllamaChatResult call(OllamaChatRequest body, OllamaChatTokenHandler tokenHandler)
throws OllamaException, IOException, InterruptedException {
this.tokenHandler = tokenHandler;
return callSync(body);
}
public OllamaChatResult callSync(OllamaChatRequest body) throws OllamaBaseException, IOException, InterruptedException {
// Create Request
public OllamaChatResult callSync(OllamaChatRequest body)
throws OllamaException, IOException, InterruptedException {
long startTime = System.currentTimeMillis();
HttpClient httpClient = HttpClient.newHttpClient();
URI uri = URI.create(getHost() + getEndpointSuffix());
URI uri = URI.create(getHost() + endpoint);
HttpRequest.Builder requestBuilder =
getRequestBuilderDefault(uri)
.POST(
body.getBodyPublisher());
getRequestBuilderDefault(uri).POST(body.getBodyPublisher());
HttpRequest request = requestBuilder.build();
LOG.debug("Asking model: {}", body);
System.out.println("Asking model: " + Utils.toJSON(body));
HttpResponse<InputStream> response =
httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
@ -101,57 +106,92 @@ public class OllamaChatEndpointCaller extends OllamaEndpointCaller {
StringBuilder thinkingBuffer = new StringBuilder();
OllamaChatResponseModel ollamaChatResponseModel = null;
List<OllamaChatToolCalls> wantedToolsForStream = null;
try (BufferedReader reader =
new BufferedReader(new InputStreamReader(responseBodyStream, StandardCharsets.UTF_8))) {
try (BufferedReader reader =
new BufferedReader(
new InputStreamReader(responseBodyStream, StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
if (statusCode == 404) {
LOG.warn("Status code: 404 (Not Found)");
OllamaErrorResponse ollamaResponseModel =
Utils.getObjectMapper().readValue(line, OllamaErrorResponse.class);
responseBuffer.append(ollamaResponseModel.getError());
} else if (statusCode == 401) {
LOG.warn("Status code: 401 (Unauthorized)");
OllamaErrorResponse ollamaResponseModel =
Utils.getObjectMapper()
.readValue("{\"error\":\"Unauthorized\"}", OllamaErrorResponse.class);
responseBuffer.append(ollamaResponseModel.getError());
} else if (statusCode == 400) {
LOG.warn("Status code: 400 (Bad Request)");
OllamaErrorResponse ollamaResponseModel = Utils.getObjectMapper().readValue(line,
OllamaErrorResponse.class);
responseBuffer.append(ollamaResponseModel.getError());
} else if (statusCode == 500) {
LOG.warn("Status code: 500 (Internal Server Error)");
OllamaErrorResponse ollamaResponseModel = Utils.getObjectMapper().readValue(line,
OllamaErrorResponse.class);
responseBuffer.append(ollamaResponseModel.getError());
} else {
boolean finished = parseResponseAndAddToBuffer(line, responseBuffer, thinkingBuffer);
ollamaChatResponseModel = Utils.getObjectMapper().readValue(line, OllamaChatResponseModel.class);
if (body.stream && ollamaChatResponseModel.getMessage().getToolCalls() != null) {
wantedToolsForStream = ollamaChatResponseModel.getMessage().getToolCalls();
}
if (finished && body.stream) {
ollamaChatResponseModel.getMessage().setContent(responseBuffer.toString());
ollamaChatResponseModel.getMessage().setThinking(thinkingBuffer.toString());
break;
}
if (handleErrorStatus(statusCode, line, responseBuffer)) {
continue;
}
boolean finished =
parseResponseAndAddToBuffer(line, responseBuffer, thinkingBuffer);
ollamaChatResponseModel =
Utils.getObjectMapper().readValue(line, OllamaChatResponseModel.class);
if (body.stream && ollamaChatResponseModel.getMessage().getToolCalls() != null) {
wantedToolsForStream = ollamaChatResponseModel.getMessage().getToolCalls();
}
if (finished && body.stream) {
ollamaChatResponseModel.getMessage().setResponse(responseBuffer.toString());
ollamaChatResponseModel.getMessage().setThinking(thinkingBuffer.toString());
break;
}
}
}
MetricsRecorder.record(
endpoint,
body.getModel(),
false,
body.isThink(),
body.isStream(),
body.getOptions(),
body.getFormat(),
startTime,
statusCode,
responseBuffer);
if (statusCode != 200) {
LOG.error("Status code " + statusCode);
throw new OllamaBaseException(responseBuffer.toString());
} else {
if (wantedToolsForStream != null) {
ollamaChatResponseModel.getMessage().setToolCalls(wantedToolsForStream);
}
OllamaChatResult ollamaResult =
new OllamaChatResult(ollamaChatResponseModel, body.getMessages());
LOG.debug("Model response: {}", ollamaResult);
return ollamaResult;
LOG.error("Status code: {}", statusCode);
System.out.println(responseBuffer);
throw new OllamaException(responseBuffer.toString());
}
if (wantedToolsForStream != null && ollamaChatResponseModel != null) {
ollamaChatResponseModel.getMessage().setToolCalls(wantedToolsForStream);
}
OllamaChatResult ollamaResult =
new OllamaChatResult(ollamaChatResponseModel, body.getMessages());
LOG.debug("Model response: {}", ollamaResult);
return ollamaResult;
}
/**
* Handles error status codes and appends error messages to the response buffer. Returns true if
* an error was handled, false otherwise.
*/
private boolean handleErrorStatus(int statusCode, String line, StringBuilder responseBuffer)
throws IOException {
switch (statusCode) {
case 404:
LOG.warn("Status code: 404 (Not Found)");
responseBuffer.append(
Utils.getObjectMapper()
.readValue(line, OllamaErrorResponse.class)
.getError());
return true;
case 401:
LOG.warn("Status code: 401 (Unauthorized)");
responseBuffer.append(
Utils.getObjectMapper()
.readValue(
"{\"error\":\"Unauthorized\"}", OllamaErrorResponse.class)
.getError());
return true;
case 400:
LOG.warn("Status code: 400 (Bad Request)");
responseBuffer.append(
Utils.getObjectMapper()
.readValue(line, OllamaErrorResponse.class)
.getError());
return true;
case 500:
LOG.warn("Status code: 500 (Internal Server Error)");
responseBuffer.append(
Utils.getObjectMapper()
.readValue(line, OllamaErrorResponse.class)
.getError());
return true;
default:
return false;
}
}
}

View File

@ -1,35 +1,49 @@
/*
* 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.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.github.ollama4j.utils.BooleanToJsonFormatFlagSerializer;
import io.github.ollama4j.utils.Utils;
import lombok.Data;
import java.util.Map;
import lombok.Data;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public abstract class OllamaCommonRequest {
protected String model;
@JsonSerialize(using = BooleanToJsonFormatFlagSerializer.class)
@JsonProperty(value = "format")
protected Boolean returnFormatJson;
protected Map<String, Object> options;
protected String template;
protected boolean stream;
@JsonProperty(value = "keep_alive")
protected String keepAlive;
protected String model;
/**
* The value can either be
* <pre>{@code json }</pre>
* or
* <pre>{@code {"key1": "val1", "key2": "val2"} }</pre>
*/
@JsonProperty(value = "format", required = false, defaultValue = "json")
protected Object format;
public String toString() {
try {
return Utils.getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
protected Map<String, Object> options;
protected String template;
protected boolean stream;
@JsonProperty(value = "keep_alive")
protected String keepAlive;
public String toString() {
try {
return Utils.getObjectMapper()
.writerWithDefaultPrettyPrinter()
.writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
}

View File

@ -1,14 +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 io.github.ollama4j.utils.Constants;
import lombok.Getter;
import java.net.URI;
import java.net.http.HttpRequest;
import java.time.Duration;
import lombok.Getter;
/**
* Abstract helperclass to call the ollama api server.
* Abstract helper class to call the ollama api server.
*/
@Getter
public abstract class OllamaEndpointCaller {
@ -17,16 +24,14 @@ public abstract class OllamaEndpointCaller {
private final Auth auth;
private final long requestTimeoutSeconds;
public OllamaEndpointCaller(String host, Auth auth, long requestTimeoutSeconds) {
protected OllamaEndpointCaller(String host, Auth auth, long requestTimeoutSeconds) {
this.host = host;
this.auth = auth;
this.requestTimeoutSeconds = requestTimeoutSeconds;
}
protected abstract String getEndpointSuffix();
protected abstract boolean parseResponseAndAddToBuffer(String line, StringBuilder responseBuffer, StringBuilder thinkingBuffer);
protected abstract boolean parseResponseAndAddToBuffer(
String line, StringBuilder responseBuffer, StringBuilder thinkingBuffer);
/**
* Get default request builder.
@ -37,7 +42,9 @@ public abstract class OllamaEndpointCaller {
protected HttpRequest.Builder getRequestBuilderDefault(URI uri) {
HttpRequest.Builder requestBuilder =
HttpRequest.newBuilder(uri)
.header(Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE, Constants.HttpConstants.APPLICATION_JSON)
.header(
Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE,
Constants.HttpConstants.APPLICATION_JSON)
.timeout(Duration.ofSeconds(this.requestTimeoutSeconds));
if (isAuthCredentialsSet()) {
requestBuilder.header("Authorization", this.auth.getAuthHeaderValue());
@ -53,5 +60,4 @@ public abstract class OllamaEndpointCaller {
protected boolean isAuthCredentialsSet() {
return this.auth != null;
}
}

View File

@ -1,17 +1,22 @@
/*
* 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 io.github.ollama4j.exceptions.OllamaBaseException;
import io.github.ollama4j.exceptions.OllamaException;
import io.github.ollama4j.models.generate.OllamaGenerateResponseModel;
import io.github.ollama4j.models.generate.OllamaGenerateStreamObserver;
import io.github.ollama4j.models.generate.OllamaStreamHandler;
import io.github.ollama4j.models.generate.OllamaGenerateTokenHandler;
import io.github.ollama4j.models.response.OllamaErrorResponse;
import io.github.ollama4j.models.response.OllamaResult;
import io.github.ollama4j.utils.OllamaRequestBody;
import io.github.ollama4j.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
@ -21,11 +26,14 @@ import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SuppressWarnings("resource")
public class OllamaGenerateEndpointCaller extends OllamaEndpointCaller {
private static final Logger LOG = LoggerFactory.getLogger(OllamaGenerateEndpointCaller.class);
public static final String endpoint = "/api/generate";
private OllamaGenerateStreamObserver responseStreamObserver;
@ -34,14 +42,11 @@ public class OllamaGenerateEndpointCaller extends OllamaEndpointCaller {
}
@Override
protected String getEndpointSuffix() {
return "/api/generate";
}
@Override
protected boolean parseResponseAndAddToBuffer(String line, StringBuilder responseBuffer, StringBuilder thinkingBuffer) {
protected boolean parseResponseAndAddToBuffer(
String line, StringBuilder responseBuffer, StringBuilder thinkingBuffer) {
try {
OllamaGenerateResponseModel ollamaResponseModel = Utils.getObjectMapper().readValue(line, OllamaGenerateResponseModel.class);
OllamaGenerateResponseModel ollamaResponseModel =
Utils.getObjectMapper().readValue(line, OllamaGenerateResponseModel.class);
if (ollamaResponseModel.getResponse() != null) {
responseBuffer.append(ollamaResponseModel.getResponse());
}
@ -58,55 +63,61 @@ public class OllamaGenerateEndpointCaller extends OllamaEndpointCaller {
}
}
public OllamaResult call(OllamaRequestBody body, OllamaStreamHandler thinkingStreamHandler, OllamaStreamHandler responseStreamHandler) throws OllamaBaseException, IOException, InterruptedException {
responseStreamObserver = new OllamaGenerateStreamObserver(thinkingStreamHandler, responseStreamHandler);
public OllamaResult call(
OllamaRequestBody body,
OllamaGenerateTokenHandler thinkingStreamHandler,
OllamaGenerateTokenHandler responseStreamHandler)
throws OllamaException, IOException, InterruptedException {
responseStreamObserver =
new OllamaGenerateStreamObserver(thinkingStreamHandler, responseStreamHandler);
return callSync(body);
}
/**
* Calls the api server on the given host and endpoint suffix asynchronously, aka waiting for the response.
* Calls the api server on the given host and endpoint suffix asynchronously, aka waiting for
* the response.
*
* @param body POST body payload
* @return result answer given by the assistant
* @throws OllamaBaseException any response code than 200 has been returned
* @throws IOException in case the responseStream can not be read
* @throws OllamaException 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
*/
@SuppressWarnings("DuplicatedCode")
public OllamaResult callSync(OllamaRequestBody body) throws OllamaBaseException, IOException, InterruptedException {
// Create Request
public OllamaResult callSync(OllamaRequestBody body)
throws OllamaException, IOException, InterruptedException {
long startTime = System.currentTimeMillis();
HttpClient httpClient = HttpClient.newHttpClient();
URI uri = URI.create(getHost() + getEndpointSuffix());
HttpRequest.Builder requestBuilder = getRequestBuilderDefault(uri).POST(body.getBodyPublisher());
URI uri = URI.create(getHost() + endpoint);
HttpRequest.Builder requestBuilder =
getRequestBuilderDefault(uri).POST(body.getBodyPublisher());
HttpRequest request = requestBuilder.build();
LOG.debug("Asking model: {}", body);
HttpResponse<InputStream> response = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
HttpResponse<InputStream> response =
httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
int statusCode = response.statusCode();
InputStream responseBodyStream = response.body();
StringBuilder responseBuffer = new StringBuilder();
StringBuilder thinkingBuffer = new StringBuilder();
OllamaGenerateResponseModel ollamaGenerateResponseModel = null;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(responseBodyStream, StandardCharsets.UTF_8))) {
try (BufferedReader reader =
new BufferedReader(
new InputStreamReader(responseBodyStream, StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
if (statusCode == 404) {
LOG.warn("Status code: 404 (Not Found)");
OllamaErrorResponse ollamaResponseModel = Utils.getObjectMapper().readValue(line, OllamaErrorResponse.class);
responseBuffer.append(ollamaResponseModel.getError());
} else if (statusCode == 401) {
LOG.warn("Status code: 401 (Unauthorized)");
OllamaErrorResponse ollamaResponseModel = Utils.getObjectMapper().readValue("{\"error\":\"Unauthorized\"}", OllamaErrorResponse.class);
responseBuffer.append(ollamaResponseModel.getError());
} else if (statusCode == 400) {
LOG.warn("Status code: 400 (Bad Request)");
OllamaErrorResponse ollamaResponseModel = Utils.getObjectMapper().readValue(line, OllamaErrorResponse.class);
if (statusCode >= 400) {
LOG.warn("Error code: {}", statusCode);
OllamaErrorResponse ollamaResponseModel =
Utils.getObjectMapper().readValue(line, OllamaErrorResponse.class);
responseBuffer.append(ollamaResponseModel.getError());
} else {
boolean finished = parseResponseAndAddToBuffer(line, responseBuffer, thinkingBuffer);
boolean finished =
parseResponseAndAddToBuffer(line, responseBuffer, thinkingBuffer);
if (finished) {
ollamaGenerateResponseModel = Utils.getObjectMapper().readValue(line, OllamaGenerateResponseModel.class);
ollamaGenerateResponseModel =
Utils.getObjectMapper()
.readValue(line, OllamaGenerateResponseModel.class);
break;
}
}
@ -115,11 +126,16 @@ public class OllamaGenerateEndpointCaller extends OllamaEndpointCaller {
if (statusCode != 200) {
LOG.error("Status code: {}", statusCode);
throw new OllamaBaseException(responseBuffer.toString());
LOG.error("Response: {}", responseBuffer);
throw new OllamaException(responseBuffer.toString());
} else {
long endTime = System.currentTimeMillis();
OllamaResult ollamaResult = new OllamaResult(responseBuffer.toString(), thinkingBuffer.toString(), endTime - startTime, statusCode);
OllamaResult ollamaResult =
new OllamaResult(
responseBuffer.toString(),
thinkingBuffer.toString(),
endTime - startTime,
statusCode);
ollamaResult.setModel(ollamaGenerateResponseModel.getModel());
ollamaResult.setCreatedAt(ollamaGenerateResponseModel.getCreatedAt());
ollamaResult.setDone(ollamaGenerateResponseModel.isDone());

View File

@ -1,17 +0,0 @@
package io.github.ollama4j.models.response;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class LibraryModel {
private String name;
private String description;
private String pullCount;
private int totalTags;
private List<String> popularTags = new ArrayList<>();
private String lastUpdated;
}

View File

@ -1,12 +0,0 @@
package io.github.ollama4j.models.response;
import lombok.Data;
import java.util.List;
@Data
public class LibraryModelDetail {
private LibraryModel model;
private List<LibraryModelTag> tags;
}

View File

@ -1,11 +0,0 @@
package io.github.ollama4j.models.response;
import lombok.Data;
@Data
public class LibraryModelTag {
private String name;
private String tag;
private String size;
private String lastUpdated;
}

View File

@ -1,8 +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.response;
import lombok.Data;
import java.util.List;
import lombok.Data;
@Data
public class ListModelsResponse {

View File

@ -1,54 +1,65 @@
/*
* 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.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.github.ollama4j.utils.Utils;
import lombok.Data;
import java.time.OffsetDateTime;
import lombok.Data;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class Model {
private String name;
private String model;
@JsonProperty("modified_at")
private OffsetDateTime modifiedAt;
@JsonProperty("expires_at")
private OffsetDateTime expiresAt;
private String digest;
private long size;
@JsonProperty("details")
private ModelMeta modelMeta;
private String name;
private String model;
@JsonProperty("modified_at")
private OffsetDateTime modifiedAt;
/**
* Returns the model name without its version
*
* @return model name
*/
public String getModelName() {
return name.split(":")[0];
}
@JsonProperty("expires_at")
private OffsetDateTime expiresAt;
/**
* Returns the model version without its name
*
* @return model version
*/
public String getModelVersion() {
return name.split(":")[1];
}
private String digest;
private long size;
@JsonProperty("details")
private ModelMeta modelMeta;
/**
* Returns the model name without its version
*
* @return model name
*/
public String getModelName() {
return name.split(":")[0];
}
/**
* Returns the model version without its name
*
* @return model version
*/
public String getModelVersion() {
return name.split(":")[1];
}
@Override
public String toString() {
try {
return Utils.getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
public String toString() {
try {
return Utils.getObjectMapper()
.writerWithDefaultPrettyPrinter()
.writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
}

View File

@ -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.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@ -9,22 +17,24 @@ import lombok.Data;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class ModelDetail {
private String license;
private String license;
@JsonProperty("modelfile")
private String modelFile;
@JsonProperty("modelfile")
private String modelFile;
private String parameters;
private String template;
private String system;
private ModelMeta details;
private String parameters;
private String template;
private String system;
private ModelMeta details;
@Override
public String toString() {
try {
return Utils.getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
public String toString() {
try {
return Utils.getObjectMapper()
.writerWithDefaultPrettyPrinter()
.writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
}

View File

@ -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.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@ -9,27 +17,29 @@ import lombok.Data;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class ModelMeta {
@JsonProperty("format")
private String format;
@JsonProperty("format")
private String format;
@JsonProperty("family")
private String family;
@JsonProperty("family")
private String family;
@JsonProperty("families")
private String[] families;
@JsonProperty("families")
private String[] families;
@JsonProperty("parameter_size")
private String parameterSize;
@JsonProperty("parameter_size")
private String parameterSize;
@JsonProperty("quantization_level")
private String quantizationLevel;
@JsonProperty("quantization_level")
private String quantizationLevel;
@Override
public String toString() {
try {
return Utils.getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
public String toString() {
try {
return Utils.getObjectMapper()
.writerWithDefaultPrettyPrinter()
.writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
}

View File

@ -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.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

View File

@ -1,15 +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.response;
import io.github.ollama4j.exceptions.OllamaBaseException;
import io.github.ollama4j.exceptions.OllamaException;
import io.github.ollama4j.models.generate.OllamaGenerateRequest;
import io.github.ollama4j.models.generate.OllamaGenerateResponseModel;
import io.github.ollama4j.utils.Constants;
import io.github.ollama4j.utils.Utils;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
@ -19,6 +22,10 @@ import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@Data
@EqualsAndHashCode(callSuper = true)
@ -31,32 +38,30 @@ public class OllamaAsyncResultStreamer extends Thread {
private String completeResponse;
private String completeThinkingResponse;
/**
* -- GETTER -- Returns the status of the request. Indicates if the request was successful or a
* failure. If the request was a failure, the `getResponse()` method will return the error
* message.
*/
@Getter
private boolean succeeded;
@Getter private boolean succeeded;
@Setter
private long requestTimeoutSeconds;
@Setter private long requestTimeoutSeconds;
/**
* -- GETTER -- Returns the HTTP response status code for the request that was made to Ollama
* server.
*/
@Getter
private int httpStatusCode;
@Getter private int httpStatusCode;
/**
* -- GETTER -- Returns the response time in milliseconds.
*/
@Getter
private long responseTime = 0;
@Getter private long responseTime = 0;
public OllamaAsyncResultStreamer(HttpRequest.Builder requestBuilder, OllamaGenerateRequest ollamaRequestModel, long requestTimeoutSeconds) {
public OllamaAsyncResultStreamer(
HttpRequest.Builder requestBuilder,
OllamaGenerateRequest ollamaRequestModel,
long requestTimeoutSeconds) {
this.requestBuilder = requestBuilder;
this.ollamaRequestModel = ollamaRequestModel;
this.completeResponse = "";
@ -70,25 +75,41 @@ public class OllamaAsyncResultStreamer extends Thread {
HttpClient httpClient = HttpClient.newHttpClient();
long startTime = System.currentTimeMillis();
try {
HttpRequest request = requestBuilder.POST(HttpRequest.BodyPublishers.ofString(Utils.getObjectMapper().writeValueAsString(ollamaRequestModel))).header(Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE, Constants.HttpConstants.APPLICATION_JSON).timeout(Duration.ofSeconds(requestTimeoutSeconds)).build();
HttpResponse<InputStream> response = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
HttpRequest request =
requestBuilder
.POST(
HttpRequest.BodyPublishers.ofString(
Utils.getObjectMapper()
.writeValueAsString(ollamaRequestModel)))
.header(
Constants.HttpConstants.HEADER_KEY_CONTENT_TYPE,
Constants.HttpConstants.APPLICATION_JSON)
.timeout(Duration.ofSeconds(requestTimeoutSeconds))
.build();
HttpResponse<InputStream> response =
httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
int statusCode = response.statusCode();
this.httpStatusCode = statusCode;
InputStream responseBodyStream = response.body();
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(responseBodyStream, StandardCharsets.UTF_8));
reader =
new BufferedReader(
new InputStreamReader(responseBodyStream, StandardCharsets.UTF_8));
String line;
StringBuilder thinkingBuffer = new StringBuilder();
StringBuilder responseBuffer = new StringBuilder();
while ((line = reader.readLine()) != null) {
if (statusCode == 404) {
OllamaErrorResponse ollamaResponseModel = Utils.getObjectMapper().readValue(line, OllamaErrorResponse.class);
OllamaErrorResponse ollamaResponseModel =
Utils.getObjectMapper().readValue(line, OllamaErrorResponse.class);
responseStream.add(ollamaResponseModel.getError());
responseBuffer.append(ollamaResponseModel.getError());
} else {
OllamaGenerateResponseModel ollamaResponseModel = Utils.getObjectMapper().readValue(line, OllamaGenerateResponseModel.class);
OllamaGenerateResponseModel ollamaResponseModel =
Utils.getObjectMapper()
.readValue(line, OllamaGenerateResponseModel.class);
String thinkingTokens = ollamaResponseModel.getThinking();
String responseTokens = ollamaResponseModel.getResponse();
if (thinkingTokens == null) {
@ -115,24 +136,27 @@ public class OllamaAsyncResultStreamer extends Thread {
try {
reader.close();
} catch (IOException e) {
// Optionally log or handle
/* do nothing */
}
}
if (responseBodyStream != null) {
try {
responseBodyStream.close();
} catch (IOException e) {
// Optionally log or handle
/* do nothing */
}
}
}
if (statusCode != 200) {
throw new OllamaBaseException(this.completeResponse);
throw new OllamaException(this.completeResponse);
}
} catch (IOException | InterruptedException | OllamaBaseException e) {
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
this.succeeded = false;
this.completeResponse = "[FAILED] " + e.getMessage();
} catch (IOException | OllamaException e) {
this.succeeded = false;
this.completeResponse = "[FAILED] " + e.getMessage();
}
}
}

View File

@ -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.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@ -7,5 +15,5 @@ import lombok.Data;
@JsonIgnoreProperties(ignoreUnknown = true)
public class OllamaErrorResponse {
private String error;
private String error;
}

View File

@ -1,21 +1,30 @@
/*
* 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.response;
import static io.github.ollama4j.utils.Utils.getObjectMapper;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.Data;
import lombok.Getter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static io.github.ollama4j.utils.Utils.getObjectMapper;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
/**
* The type Ollama result.
*/
@Getter
@Setter
@SuppressWarnings("unused")
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@ -24,14 +33,17 @@ public class OllamaResult {
* Get the completion/response text
*/
private final String response;
/**
* Get the thinking text (if available)
*/
private final String thinking;
/**
* Get the response status code.
*/
private int httpStatusCode;
/**
* Get the response time in milliseconds.
*/
@ -75,7 +87,9 @@ public class OllamaResult {
responseMap.put("promptEvalDuration", this.promptEvalDuration);
responseMap.put("evalCount", this.evalCount);
responseMap.put("evalDuration", this.evalDuration);
return getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(responseMap);
return getObjectMapper()
.writerWithDefaultPrettyPrinter()
.writeValueAsString(responseMap);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
@ -95,17 +109,16 @@ public class OllamaResult {
try {
// Check if the response is a valid JSON
if ((!responseStr.trim().startsWith("{") && !responseStr.trim().startsWith("[")) ||
(!responseStr.trim().endsWith("}") && !responseStr.trim().endsWith("]"))) {
if ((!responseStr.trim().startsWith("{") && !responseStr.trim().startsWith("["))
|| (!responseStr.trim().endsWith("}") && !responseStr.trim().endsWith("]"))) {
throw new IllegalArgumentException("Response is not a valid JSON object");
}
Map<String, Object> response = getObjectMapper().readValue(responseStr,
new TypeReference<Map<String, Object>>() {
});
return response;
return getObjectMapper()
.readValue(responseStr, new TypeReference<Map<String, Object>>() {});
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("Failed to parse response as JSON: " + e.getMessage(), e);
throw new IllegalArgumentException(
"Failed to parse response as JSON: " + e.getMessage(), e);
}
}
@ -126,13 +139,14 @@ public class OllamaResult {
try {
// Check if the response is a valid JSON
if ((!responseStr.trim().startsWith("{") && !responseStr.trim().startsWith("[")) ||
(!responseStr.trim().endsWith("}") && !responseStr.trim().endsWith("]"))) {
if ((!responseStr.trim().startsWith("{") && !responseStr.trim().startsWith("["))
|| (!responseStr.trim().endsWith("}") && !responseStr.trim().endsWith("]"))) {
throw new IllegalArgumentException("Response is not a valid JSON object");
}
return getObjectMapper().readValue(responseStr, clazz);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("Failed to parse response as JSON: " + e.getMessage(), e);
throw new IllegalArgumentException(
"Failed to parse response as JSON: " + e.getMessage(), e);
}
}
}

View File

@ -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.response;
import java.util.Iterator;

View File

@ -1,85 +1,90 @@
/*
* 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.response;
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.core.type.TypeReference;
import java.util.List;
import java.util.Map;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
import static io.github.ollama4j.utils.Utils.getObjectMapper;
@Getter
@SuppressWarnings("unused")
@Data
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class OllamaStructuredResult {
private String response;
private String thinking;
private int httpStatusCode;
private long responseTime = 0;
private String model;
private String response;
private String thinking;
private int httpStatusCode;
private long responseTime = 0;
private String model;
private @JsonProperty("created_at") String createdAt;
private boolean done;
private @JsonProperty("done_reason") String doneReason;
private List<Integer> 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("created_at") String createdAt;
private boolean done;
private @JsonProperty("done_reason") String doneReason;
private List<Integer> 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;
public OllamaStructuredResult(String response, long responseTime, int httpStatusCode) {
this.response = response;
this.responseTime = responseTime;
this.httpStatusCode = httpStatusCode;
}
@Override
public String toString() {
try {
return getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
public OllamaStructuredResult(String response, long responseTime, int httpStatusCode) {
this.response = response;
this.responseTime = responseTime;
this.httpStatusCode = httpStatusCode;
}
}
/**
* Get the structured response if the response is a JSON object.
*
* @return Map - structured response
*/
public Map<String, Object> getStructuredResponse() {
try {
Map<String, Object> response = getObjectMapper().readValue(this.getResponse(),
new TypeReference<Map<String, Object>>() {
});
return response;
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
@Override
public String toString() {
try {
return getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
/**
* Get the structured response mapped to a specific class type.
*
* @param <T> The type of class to map the response to
* @param clazz The class to map the response to
* @return An instance of the specified class with the response data
* @throws RuntimeException if there is an error mapping the response
*/
public <T> T getStructuredResponse(Class<T> clazz) {
try {
return getObjectMapper().readValue(this.getResponse(), clazz);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
/**
* Get the structured response if the response is a JSON object.
*
* @return Map - structured response
*/
public Map<String, Object> getStructuredResponse() {
try {
return getObjectMapper()
.readValue(this.getResponse(), new TypeReference<Map<String, Object>>() {});
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
/**
* Get the structured response mapped to a specific class type.
*
* @param <T> The type of class to map the response to
* @param clazz The class to map the response to
* @return An instance of the specified class with the response data
* @throws RuntimeException if there is an error mapping the response
*/
public <T> T getStructuredResponse(Class<T> clazz) {
try {
return getObjectMapper().readValue(this.getResponse(), clazz);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
}

View File

@ -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.response;
import lombok.Data;

View File

@ -1,18 +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.tools;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Map;
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class OllamaToolCallsFunction
{
public class OllamaToolCallsFunction {
private String name;
private Map<String,Object> arguments;
private Map<String, Object> arguments;
}

View File

@ -1,13 +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.tools;
import io.github.ollama4j.models.response.OllamaResult;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@ -22,7 +29,8 @@ public class OllamaToolsResult {
return results;
}
for (Map.Entry<ToolFunctionCallSpec, Object> r : this.toolResults.entrySet()) {
results.add(new ToolResult(r.getKey().getName(), r.getKey().getArguments(), r.getValue()));
results.add(
new ToolResult(r.getKey().getName(), r.getKey().getArguments(), r.getValue()));
}
return results;
}

View File

@ -1,13 +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.tools;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
/**
* Specification of a {@link ToolFunction} that provides the implementation via java reflection calling.
@ -15,17 +22,18 @@ import java.util.Map;
@Setter
@Getter
@AllArgsConstructor
public class ReflectionalToolFunction implements ToolFunction{
public class ReflectionalToolFunction implements ToolFunction {
private Object functionHolder;
private Method function;
private LinkedHashMap<String,String> propertyDefinition;
private LinkedHashMap<String, String> propertyDefinition;
@Override
public Object apply(Map<String, Object> arguments) {
LinkedHashMap<String, Object> argumentsCopy = new LinkedHashMap<>(this.propertyDefinition);
for (Map.Entry<String,String> param : this.propertyDefinition.entrySet()){
argumentsCopy.replace(param.getKey(),typeCast(arguments.get(param.getKey()),param.getValue()));
for (Map.Entry<String, String> param : this.propertyDefinition.entrySet()) {
argumentsCopy.replace(
param.getKey(), typeCast(arguments.get(param.getKey()), param.getValue()));
}
try {
return function.invoke(functionHolder, argumentsCopy.values().toArray());
@ -35,7 +43,7 @@ public class ReflectionalToolFunction implements ToolFunction{
}
private Object typeCast(Object inputValue, String className) {
if(className == null || inputValue == null) {
if (className == null || inputValue == null) {
return null;
}
String inputValueString = inputValue.toString();
@ -50,5 +58,4 @@ public class ReflectionalToolFunction implements ToolFunction{
return inputValueString;
}
}
}

View File

@ -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.tools;
import java.util.Map;

View File

@ -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.tools;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Map;
@Data
@AllArgsConstructor
@NoArgsConstructor

View File

@ -1,28 +1,47 @@
/*
* 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.tools;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import io.github.ollama4j.exceptions.ToolNotFoundException;
import java.util.*;
public class ToolRegistry {
private final Map<String, Tools.ToolSpecification> tools = new HashMap<>();
private final List<Tools.Tool> tools = new ArrayList<>();
public ToolFunction getToolFunction(String name) {
final Tools.ToolSpecification toolSpecification = tools.get(name);
return toolSpecification != null ? toolSpecification.getToolFunction() : null;
public ToolFunction getToolFunction(String name) throws ToolNotFoundException {
for (Tools.Tool tool : tools) {
if (tool.getToolSpec().getName().equals(name)) {
return tool.getToolFunction();
}
}
throw new ToolNotFoundException(String.format("Tool '%s' not found.", name));
}
public void addTool(String name, Tools.ToolSpecification specification) {
tools.put(name, specification);
public void addTool(Tools.Tool tool) {
try {
getToolFunction(tool.getToolSpec().getName());
} catch (ToolNotFoundException e) {
tools.add(tool);
}
}
public Collection<Tools.ToolSpecification> getRegisteredSpecs() {
return tools.values();
public void addTools(List<Tools.Tool> tools) {
for (Tools.Tool tool : tools) {
addTool(tool);
}
}
/**
* Removes all registered tools from the registry.
*/
public List<Tools.Tool> getRegisteredTools() {
return tools;
}
/** Removes all registered tools from the registry. */
public void clear() {
tools.clear();
}

View File

@ -1,126 +1,119 @@
/*
* 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.tools;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.github.ollama4j.utils.Utils;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Tools {
@Data
@Builder
public static class ToolSpecification {
private String functionName;
private String functionDescription;
private PromptFuncDefinition toolPrompt;
private ToolFunction toolFunction;
}
private Tools() {}
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class PromptFuncDefinition {
private String type;
private PromptFuncSpec function;
public static class Tool {
@JsonProperty("function")
private ToolSpec toolSpec;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class PromptFuncSpec {
private String name;
private String description;
private Parameters parameters;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class Parameters {
private String type;
private Map<String, Property> properties;
private List<String> required;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class Property {
private String type;
private String description;
@JsonProperty("enum")
@JsonInclude(JsonInclude.Include.NON_NULL)
private List<String> enumValues;
@JsonIgnore
private boolean required;
}
@Builder.Default private String type = "function";
@JsonIgnore private ToolFunction toolFunction;
}
public static class PropsBuilder {
private final Map<String, PromptFuncDefinition.Property> props = new HashMap<>();
public PropsBuilder withProperty(String key, PromptFuncDefinition.Property property) {
props.put(key, property);
return this;
}
public Map<String, PromptFuncDefinition.Property> build() {
return props;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class ToolSpec {
private String name;
private String description;
private Parameters parameters;
}
public static class PromptBuilder {
private final List<PromptFuncDefinition> tools = new ArrayList<>();
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Parameters {
private Map<String, Property> properties;
private List<String> required = new ArrayList<>();
private String promptText;
public String build() throws JsonProcessingException {
return "[AVAILABLE_TOOLS] " + Utils.getObjectMapper().writeValueAsString(tools) + "[/AVAILABLE_TOOLS][INST] " + promptText + " [/INST]";
}
public PromptBuilder withPrompt(String prompt) throws JsonProcessingException {
promptText = prompt;
return this;
}
public PromptBuilder withToolSpecification(ToolSpecification spec) {
PromptFuncDefinition def = new PromptFuncDefinition();
def.setType("function");
PromptFuncDefinition.PromptFuncSpec functionDetail = new PromptFuncDefinition.PromptFuncSpec();
functionDetail.setName(spec.getFunctionName());
functionDetail.setDescription(spec.getFunctionDescription());
PromptFuncDefinition.Parameters parameters = new PromptFuncDefinition.Parameters();
parameters.setType("object");
parameters.setProperties(spec.getToolPrompt().getFunction().parameters.getProperties());
List<String> requiredValues = new ArrayList<>();
for (Map.Entry<String, PromptFuncDefinition.Property> p : spec.getToolPrompt().getFunction().getParameters().getProperties().entrySet()) {
if (p.getValue().isRequired()) {
requiredValues.add(p.getKey());
public static Parameters of(Map<String, Property> properties) {
Parameters params = new Parameters();
params.setProperties(properties);
// Optionally, populate required from properties' required flags
if (properties != null) {
for (Map.Entry<String, Property> entry : properties.entrySet()) {
if (entry.getValue() != null && entry.getValue().isRequired()) {
params.getRequired().add(entry.getKey());
}
}
}
parameters.setRequired(requiredValues);
functionDetail.setParameters(parameters);
def.setFunction(functionDetail);
return params;
}
tools.add(def);
return this;
@Override
public String toString() {
ObjectNode node =
com.fasterxml.jackson.databind.json.JsonMapper.builder()
.build()
.createObjectNode();
node.put("type", "object");
if (properties != null) {
ObjectNode propsNode = node.putObject("properties");
for (Map.Entry<String, Property> entry : properties.entrySet()) {
ObjectNode propNode = propsNode.putObject(entry.getKey());
Property prop = entry.getValue();
propNode.put("type", prop.getType());
propNode.put("description", prop.getDescription());
if (prop.getEnumValues() != null) {
propNode.putArray("enum")
.addAll(
prop.getEnumValues().stream()
.map(
com.fasterxml.jackson.databind.node.TextNode
::new)
.collect(java.util.stream.Collectors.toList()));
}
}
}
if (required != null && !required.isEmpty()) {
node.putArray("required")
.addAll(
required.stream()
.map(com.fasterxml.jackson.databind.node.TextNode::new)
.collect(java.util.stream.Collectors.toList()));
}
return node.toPrettyString();
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class Property {
private String type;
private String description;
@JsonProperty("enum")
@JsonInclude(JsonInclude.Include.NON_NULL)
private List<String> enumValues;
@JsonIgnore private boolean required;
}
}

View File

@ -1,23 +1,37 @@
/*
* 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.tools.annotations;
import io.github.ollama4j.OllamaAPI;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotates a class that calls {@link io.github.ollama4j.OllamaAPI} such that the Method
* {@link OllamaAPI#registerAnnotatedTools()} can be used to auto-register all provided classes (resp. all
* contained Methods of the provider classes annotated with {@link ToolSpec}).
* Annotation to mark a class as an Ollama tool service.
* <p>
* When a class is annotated with {@code @OllamaToolService}, the method
* {@link OllamaAPI#registerAnnotatedTools()} can be used to automatically register all tool provider
* classes specified in the {@link #providers()} array. All methods in those provider classes that are
* annotated with {@link ToolSpec} will be registered as tools.
* </p>
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface OllamaToolService {
/**
* @return Classes with no-arg constructor that will be used for tool-registration.
* Specifies the provider classes whose methods annotated with {@link ToolSpec} should be registered as tools.
* Each provider class must have a public no-argument constructor.
*
* @return an array of provider classes to be used for tool registration
*/
Class<?>[] providers();
}

View File

@ -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.tools.annotations;
import java.lang.annotation.ElementType;

View File

@ -1,28 +1,44 @@
/*
* 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.tools.annotations;
import io.github.ollama4j.OllamaAPI;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotates Methods of classes that should be registered as tools by {@link OllamaAPI#registerAnnotatedTools()}
* automatically.
* Annotation to mark a method as a tool that can be registered automatically by
* {@link OllamaAPI#registerAnnotatedTools()}.
* <p>
* Methods annotated with {@code @ToolSpec} will be discovered and registered as tools
* when the containing class is specified as a provider in {@link OllamaToolService}.
* </p>
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ToolSpec {
/**
* @return tool-name that the method should be used as. Defaults to the methods name.
* Specifies the name of the tool as exposed to the LLM.
* If left empty, the method's name will be used as the tool name.
*
* @return the tool name
*/
String name() default "";
/**
* @return a detailed description of the method that can be interpreted by the llm, whether it should call the tool
* or not.
* Provides a detailed description of the tool's functionality.
* This description is used by the LLM to determine when to call the tool.
*
* @return the tool description
*/
String desc();
}

View File

@ -1,54 +0,0 @@
package io.github.ollama4j.tools.sampletools;
import io.github.ollama4j.tools.Tools;
import java.util.Map;
@SuppressWarnings("resource")
public class WeatherTool {
private String paramCityName = "cityName";
public WeatherTool() {
}
public String getCurrentWeather(Map<String, Object> arguments) {
String city = (String) arguments.get(paramCityName);
return "It is sunny in " + city;
}
public Tools.ToolSpecification getSpecification() {
return Tools.ToolSpecification.builder()
.functionName("weather-reporter")
.functionDescription(
"You are a tool who simply finds the city name from the user's message input/query about weather.")
.toolFunction(this::getCurrentWeather)
.toolPrompt(
Tools.PromptFuncDefinition.builder()
.type("prompt")
.function(
Tools.PromptFuncDefinition.PromptFuncSpec
.builder()
.name("get-city-name")
.description("Get the city name")
.parameters(
Tools.PromptFuncDefinition.Parameters
.builder()
.type("object")
.properties(
Map.of(
paramCityName,
Tools.PromptFuncDefinition.Property
.builder()
.type("string")
.description(
"The name of the city. e.g. Bengaluru")
.required(true)
.build()))
.required(java.util.List
.of(paramCityName))
.build())
.build())
.build())
.build();
}
}

Some files were not shown because too many files have changed in this diff Show More