mirror of
https://github.com/amithkoujalgi/ollama4j.git
synced 2025-10-27 06:30:42 +01:00
Compare commits
104 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d091d1826 | ||
|
|
9fd77a6743 | ||
|
|
57a962148b | ||
|
|
3c64f2099f | ||
|
|
a9c7f4e5e0 | ||
|
|
e7f58d4e0d | ||
|
|
7ef859bba5 | ||
|
|
3c30593e1e | ||
|
|
98b794ca2b | ||
|
|
bc90a15a68 | ||
|
|
bb4689e94b | ||
|
|
6739c93edc | ||
|
|
c8c30d703b | ||
|
|
419b0369c9 | ||
|
|
d9a94b95e1 | ||
|
|
db1db948c8 | ||
|
|
41ad780224 | ||
|
|
cb58c6a9b0 | ||
|
|
d1115e0b35 | ||
|
|
740bd3750b | ||
|
|
c9aa6c9e08 | ||
|
|
71bba6ee0d | ||
|
|
27b2201ff9 | ||
|
|
23d23c4ad7 | ||
|
|
e409ff1cf9 | ||
|
|
9a12cebb68 | ||
|
|
24f5bc4fec | ||
|
|
d7c313417b | ||
|
|
b67b4c7eb5 | ||
|
|
ab70201844 | ||
|
|
ac8a40a017 | ||
|
|
1ac65f821b | ||
|
|
d603c4b94b | ||
|
|
a418cbc1dc | ||
|
|
785dd12730 | ||
|
|
dda807d818 | ||
|
|
a06a4025fa | ||
| 761fbc3398 | |||
| a96dc11679 | |||
| b2b3febdaa | |||
|
|
f27bea11d5 | ||
|
|
9503451d5a | ||
|
|
04bae4ca6a | ||
|
|
3e33b8df62 | ||
|
|
a494053263 | ||
|
|
260c57ca84 | ||
|
|
db008de0ca | ||
|
|
1b38466f44 | ||
|
|
26ec00dab8 | ||
|
|
5e6971cc4a | ||
|
|
8b3417ecda | ||
|
|
35f5f34196 | ||
|
|
d8c3edd55f | ||
|
|
7ffbc5d3f2 | ||
|
|
c4b7830614 | ||
|
|
69f6fd81cf | ||
|
|
b6a293add7 | ||
|
|
25694a8bc9 | ||
|
|
12bb10392e | ||
|
|
e9c33ab0b2 | ||
|
|
903a8176cd | ||
|
|
4a91918e84 | ||
|
|
ff3344616c | ||
|
|
726fea5b74 | ||
|
|
a09f1362e9 | ||
|
|
4ef0821932 | ||
|
|
2d3cf228cb | ||
|
|
5b3713c69e | ||
|
|
e9486cbb8e | ||
|
|
057f0babeb | ||
|
|
da146640ca | ||
|
|
82be761b86 | ||
|
|
9c3fc49df1 | ||
|
|
5f19eb17ac | ||
|
|
ecb04d6d82 | ||
|
|
3fc7e9423c | ||
|
|
405a08b330 | ||
|
|
921f745435 | ||
|
|
bedfec6bf9 | ||
|
|
afa09e87a5 | ||
|
|
baf2320ea6 | ||
|
|
948a7444fb | ||
|
|
ec0eb8b469 | ||
|
|
8f33de7e59 | ||
|
|
8c59e6511b | ||
|
|
b93fc7623a | ||
|
|
bd1a57c7e0 | ||
|
|
7fabead249 | ||
|
|
268a973d5e | ||
|
|
d949a3cb69 | ||
|
|
e2443ed68a | ||
|
|
37193b1f5b | ||
|
|
e33071ae38 | ||
|
|
fffc8dc526 | ||
|
|
def950cc9c | ||
|
|
f4db7ca326 | ||
|
|
18760250ea | ||
|
|
233597efd1 | ||
|
|
cec9f29eb7 | ||
|
|
20cb92a418 | ||
|
|
b0dc38954b | ||
|
|
1479d0a494 | ||
|
|
b328daee43 | ||
|
|
b90c8bc622 |
24
.github/workflows/label-issue-stale.yml
vendored
Normal file
24
.github/workflows/label-issue-stale.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: Mark stale issues
|
||||
|
||||
on:
|
||||
workflow_dispatch: # for manual run
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # Runs every day at midnight
|
||||
|
||||
permissions:
|
||||
contents: write # only for delete-branch option
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Mark stale issues
|
||||
uses: actions/stale@v8
|
||||
with:
|
||||
repo-token: ${{ github.token }}
|
||||
days-before-stale: 15
|
||||
stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.'
|
||||
days-before-close: 7
|
||||
stale-issue-label: 'stale'
|
||||
exempt-issue-labels: 'pinned,security'
|
||||
8
.github/workflows/publish-docs.yml
vendored
8
.github/workflows/publish-docs.yml
vendored
@@ -1,5 +1,5 @@
|
||||
# Simple workflow for deploying static content to GitHub Pages
|
||||
name: Deploy Docs to GH Pages
|
||||
name: Publish Docs to GH Pages
|
||||
|
||||
on:
|
||||
release:
|
||||
@@ -63,12 +63,12 @@ jobs:
|
||||
working-directory: "."
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v3
|
||||
uses: actions/configure-pages@v5
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v2
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
# Upload entire repository
|
||||
path: './docs/build/.'
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v2
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
36
.github/workflows/run-tests.yml
vendored
Normal file
36
.github/workflows/run-tests.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: Run Unit and Integration Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: 'Branch name to run the tests on'
|
||||
required: true
|
||||
default: 'main'
|
||||
|
||||
jobs:
|
||||
run-tests:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.inputs.branch }}
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
server-id: github
|
||||
settings-path: ${{ github.workspace }}
|
||||
|
||||
- name: Run unit tests
|
||||
run: mvn clean test -Punit-tests
|
||||
|
||||
- name: Run integration tests
|
||||
run: mvn clean verify -Pintegration-tests
|
||||
4
Makefile
4
Makefile
@@ -11,9 +11,9 @@ doxygen:
|
||||
doxygen Doxyfile
|
||||
|
||||
list-releases:
|
||||
curl 'https://central.sonatype.com/api/internal/browse/component/versions?sortField=normalizedVersion&sortDirection=asc&page=0&size=12&filter=namespace%3Aio.github.amithkoujalgi%2Cname%3Aollama4j' \
|
||||
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 '.components[].version'
|
||||
--silent | jq -r '.components[].version'
|
||||
|
||||
build-docs:
|
||||
npm i --prefix docs && npm run build --prefix docs
|
||||
|
||||
127
README.md
127
README.md
@@ -4,13 +4,11 @@
|
||||
<img src='https://raw.githubusercontent.com/ollama4j/ollama4j/65a9d526150da8fcd98e2af6a164f055572bf722/ollama4j.jpeg' width='100' alt="ollama4j-icon">
|
||||
</p>
|
||||
|
||||
|
||||
A Java library (wrapper/binding) for [Ollama](https://ollama.ai/) server.
|
||||
<div align="center">
|
||||
A Java library (wrapper/binding) for Ollama server.
|
||||
|
||||
Find more details on the [website](https://ollama4j.github.io/ollama4j/).
|
||||
|
||||
<div align="center">
|
||||
|
||||

|
||||

|
||||

|
||||
@@ -33,8 +31,11 @@ Find more details on the [website](https://ollama4j.github.io/ollama4j/).
|
||||
|
||||

|
||||
[](https://codecov.io/gh/ollama4j/ollama4j)
|
||||
|
||||
[](https://github.com/ollama4j/ollama4j/actions/workflows/run-tests.yml)
|
||||

|
||||
|
||||
|
||||
</div>
|
||||
|
||||
[//]: # ()
|
||||
@@ -47,6 +48,7 @@ Find more details on the [website](https://ollama4j.github.io/ollama4j/).
|
||||
- [Requirements](#requirements)
|
||||
- [Installation](#installation)
|
||||
- [API Spec](https://ollama4j.github.io/ollama4j/category/apis---model-management)
|
||||
- [Examples](#examples)
|
||||
- [Javadoc](https://ollama4j.github.io/ollama4j/apidocs/)
|
||||
- [Development](#development)
|
||||
- [Contributions](#get-involved)
|
||||
@@ -75,61 +77,6 @@ Find more details on the [website](https://ollama4j.github.io/ollama4j/).
|
||||
<img src="https://img.shields.io/badge/v0.3.0-green.svg?style=for-the-badge&labelColor=gray&label=Ollama&color=blue" alt=""/>
|
||||
</a>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
<a href="https://ollama.ai/" target="_blank">Local Installation</a>
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
<a href="https://hub.docker.com/r/ollama/ollama" target="_blank">Docker Installation</a>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
<a href="https://ollama.com/download/Ollama-darwin.zip" target="_blank">Download for macOS</a>
|
||||
|
||||
<a href="https://ollama.com/download/OllamaSetup.exe" target="_blank">Download for Windows</a>
|
||||
|
||||
Install on Linux
|
||||
|
||||
```shell
|
||||
curl -fsSL https://ollama.com/install.sh | sh
|
||||
```
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
|
||||
|
||||
CPU only
|
||||
|
||||
```shell
|
||||
docker run -d -p 11434:11434 \
|
||||
-v ollama:/root/.ollama \
|
||||
--name ollama \
|
||||
ollama/ollama
|
||||
```
|
||||
|
||||
NVIDIA GPU
|
||||
|
||||
```shell
|
||||
docker run -d -p 11434:11434 \
|
||||
--gpus=all \
|
||||
-v ollama:/root/.ollama \
|
||||
--name ollama \
|
||||
ollama/ollama
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Installation
|
||||
|
||||
> [!NOTE]
|
||||
@@ -155,7 +102,7 @@ In your Maven project, add this dependency:
|
||||
<dependency>
|
||||
<groupId>io.github.ollama4j</groupId>
|
||||
<artifactId>ollama4j</artifactId>
|
||||
<version>1.0.79</version>
|
||||
<version>1.0.93</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
@@ -211,7 +158,7 @@ In your Maven project, add this dependency:
|
||||
<dependency>
|
||||
<groupId>io.github.ollama4j</groupId>
|
||||
<artifactId>ollama4j</artifactId>
|
||||
<version>1.0.79</version>
|
||||
<version>1.0.93</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
@@ -221,7 +168,7 @@ In your Maven project, add this dependency:
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
implementation 'io.github.ollama4j:ollama4j:1.0.79'
|
||||
implementation 'io.github.ollama4j:ollama4j:1.0.93'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -268,27 +215,35 @@ make integration-tests
|
||||
|
||||
Newer artifacts are published via GitHub Actions CI workflow when a new release is created from `main` branch.
|
||||
|
||||
#### Who's using Ollama4j?
|
||||
## Examples
|
||||
|
||||
- `Datafaker`: a library to generate fake data
|
||||
- https://github.com/datafaker-net/datafaker-experimental/tree/main/ollama-api
|
||||
- `Vaadin Web UI`: UI-Tester for Interactions with Ollama via ollama4j
|
||||
- https://github.com/TEAMPB/ollama4j-vaadin-ui
|
||||
- `ollama-translator`: Minecraft 1.20.6 spigot plugin allows to easily break language barriers by using ollama on the
|
||||
server to translate all messages into a specfic target language.
|
||||
- https://github.com/liebki/ollama-translator
|
||||
- https://www.reddit.com/r/fabricmc/comments/1e65x5s/comment/ldr2vcf/
|
||||
- `Ollama4j Web UI`: A web UI for Ollama written in Java using Spring Boot and Vaadin framework and
|
||||
Ollama4j.
|
||||
- https://github.com/ollama4j/ollama4j-web-ui
|
||||
- `JnsCLI`: A command-line tool for Jenkins that manages jobs, builds, and configurations directly from the terminal while offering AI-powered error analysis for quick troubleshooting.
|
||||
- https://github.com/mirum8/jnscli
|
||||
The `ollama4j-examples` repository contains examples for using the Ollama4j library. You can explore it [here](https://github.com/ollama4j/ollama4j-examples).
|
||||
|
||||
#### Traction
|
||||
## ⭐ Give us a Star!
|
||||
|
||||
If you like or are using this project to build your own, please give us a star. It's a free way to show your support.
|
||||
|
||||
## Who's using Ollama4j?
|
||||
|
||||
| # | Project Name | Description | Link |
|
||||
|----|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 1 | Datafaker | A library to generate fake data | [GitHub](https://github.com/datafaker-net/datafaker-experimental/tree/main/ollama-api) |
|
||||
| 2 | Vaadin Web UI | UI-Tester for interactions with Ollama via ollama4j | [GitHub](https://github.com/TEAMPB/ollama4j-vaadin-ui) |
|
||||
| 3 | ollama-translator | A Minecraft 1.20.6 Spigot plugin that translates all messages into a specific target language via Ollama | [GitHub](https://github.com/liebki/ollama-translator) |
|
||||
| 4 | AI Player | A Minecraft mod that adds an intelligent "second player" to the game | [Website](https://modrinth.com/mod/ai-player), [GitHub](https://github.com/shasankp000/AI-Player), <br/> [Reddit Thread](https://www.reddit.com/r/fabricmc/comments/1e65x5s/comment/ldr2vcf/) |
|
||||
| 5 | Ollama4j Web UI | A web UI for Ollama written in Java using Spring Boot, Vaadin, and Ollama4j | [GitHub](https://github.com/ollama4j/ollama4j-web-ui) |
|
||||
| 6 | JnsCLI | A command-line tool for Jenkins that manages jobs, builds, and configurations, with AI-powered error analysis | [GitHub](https://github.com/mirum8/jnscli) |
|
||||
| 7 | Katie Backend | An open-source AI-based question-answering platform for accessing private domain knowledge | [GitHub](https://github.com/wyona/katie-backend) |
|
||||
| 8 | TeleLlama3 Bot | A question-answering Telegram bot | [Repo](https://git.hiast.edu.sy/mohamadbashar.disoki/telellama3-bot) |
|
||||
| 9 | moqui-wechat | A moqui-wechat component | [GitHub](https://github.com/heguangyong/moqui-wechat) |
|
||||
| 10 | B4X | A set of simple and powerful RAD tool for Desktop and Server development | [Website](https://www.b4x.com/android/forum/threads/ollama4j-library-pnd_ollama4j-your-local-offline-llm-like-chatgpt.165003/) |
|
||||
| 11 | Research Article | Article: `Large language model based mutations in genetic improvement` - published on National Library of Medicine (National Center for Biotechnology Information) | [Website](https://pmc.ncbi.nlm.nih.gov/articles/PMC11750896/) |
|
||||
|
||||
## Traction
|
||||
|
||||
[](https://star-history.com/#ollama4j/ollama4j&Date)
|
||||
|
||||
### Get Involved
|
||||
## Get Involved
|
||||
|
||||
<div align="center">
|
||||
|
||||
@@ -316,6 +271,22 @@ Contributions are most welcome! Whether it's reporting a bug, proposing an enhan
|
||||
with code - any sort
|
||||
of contribution is much appreciated.
|
||||
|
||||
## 🏷️ License and Citation
|
||||
|
||||
The code is available under [MIT License](./LICENSE).
|
||||
|
||||
If you find this project helpful in your research, please cite this work at
|
||||
|
||||
```
|
||||
@misc{ollama4j2024,
|
||||
author = {Amith Koujalgi},
|
||||
title = {Ollama4j: A Java Library (Wrapper/Binding) for Ollama Server},
|
||||
year = {2024},
|
||||
month = {January},
|
||||
url = {https://github.com/ollama4j/ollama4j}
|
||||
}
|
||||
```
|
||||
|
||||
### References
|
||||
|
||||
- [Ollama REST APIs](https://github.com/jmorganca/ollama/blob/main/docs/api.md)
|
||||
|
||||
@@ -7,6 +7,8 @@ sidebar_position: 7
|
||||
This API lets you create a conversation with LLMs. Using this API enables you to ask questions to the model including
|
||||
information using the history of already asked questions and the respective answers.
|
||||
|
||||
|
||||
|
||||
## Create a new conversation and use chat history to augment follow up questions
|
||||
|
||||
```java
|
||||
@@ -33,7 +35,7 @@ public class Main {
|
||||
// start conversation with model
|
||||
OllamaChatResult chatResult = ollamaAPI.chat(requestModel);
|
||||
|
||||
System.out.println("First answer: " + chatResult.getResponse());
|
||||
System.out.println("First answer: " + chatResult.getResponseModel().getMessage().getContent());
|
||||
|
||||
// create next userQuestion
|
||||
requestModel = builder.withMessages(chatResult.getChatHistory()).withMessage(OllamaChatMessageRole.USER, "And what is the second largest city?").build();
|
||||
@@ -41,7 +43,7 @@ public class Main {
|
||||
// "continue" conversation with model
|
||||
chatResult = ollamaAPI.chat(requestModel);
|
||||
|
||||
System.out.println("Second answer: " + chatResult.getResponse());
|
||||
System.out.println("Second answer: " + chatResult.getResponseModel().getMessage().getContent());
|
||||
|
||||
System.out.println("Chat History: " + chatResult.getChatHistory());
|
||||
}
|
||||
@@ -205,7 +207,7 @@ public class Main {
|
||||
// start conversation with model
|
||||
OllamaChatResult chatResult = ollamaAPI.chat(requestModel);
|
||||
|
||||
System.out.println(chatResult.getResponse());
|
||||
System.out.println(chatResult.getResponseModel());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +246,7 @@ public class Main {
|
||||
new File("/path/to/image"))).build();
|
||||
|
||||
OllamaChatResult chatResult = ollamaAPI.chat(requestModel);
|
||||
System.out.println("First answer: " + chatResult.getResponse());
|
||||
System.out.println("First answer: " + chatResult.getResponseModel());
|
||||
|
||||
builder.reset();
|
||||
|
||||
@@ -254,7 +256,7 @@ public class Main {
|
||||
.withMessage(OllamaChatMessageRole.USER, "What's the dogs breed?").build();
|
||||
|
||||
chatResult = ollamaAPI.chat(requestModel);
|
||||
System.out.println("Second answer: " + chatResult.getResponse());
|
||||
System.out.println("Second answer: " + chatResult.getResponseModel());
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -269,4 +271,12 @@ You will get a response similar to:
|
||||
> Second Answer: Based on the image, it's difficult to definitively determine the breed of the dog. However, the dog
|
||||
> 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.
|
||||
> confidently.
|
||||
|
||||
|
||||
[//]: # (Generated using: https://emgithub.com/)
|
||||
<iframe style={{ width: '100%', height: '919px', border: 'none' }} allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Follama4j%2Follama4j-examples%2Fblob%2Fmain%2Fsrc%2Fmain%2Fjava%2Fio%2Fgithub%2Follama4j%2Fexamples%2FChatExample.java&style=default&type=code&showBorder=on&showLineNumbers=on&showFileMeta=on&showFullPath=on&showCopy=on" />
|
||||
|
||||
<a href="https://github.com/ollama4j/ollama4j-examples/blob/main/src/main/java/io/github/ollama4j/examples/ChatExample.java" target="_blank">
|
||||
View ChatExample.java on GitHub
|
||||
</a>
|
||||
65
docs/docs/apis-generate/custom-roles.md
Normal file
65
docs/docs/apis-generate/custom-roles.md
Normal file
@@ -0,0 +1,65 @@
|
||||
---
|
||||
sidebar_position: 8
|
||||
---
|
||||
|
||||
# Custom Roles
|
||||
|
||||
Allows to manage custom roles (apart from the base roles) for chat interactions with the models.
|
||||
|
||||
_Particularly helpful when you would need to use different roles that the newer models support other than the base
|
||||
roles._
|
||||
|
||||
_Base roles are `SYSTEM`, `USER`, `ASSISTANT`, `TOOL`._
|
||||
|
||||
### Usage
|
||||
|
||||
#### Add new role
|
||||
|
||||
```java
|
||||
import io.github.ollama4j.OllamaAPI;
|
||||
import io.github.ollama4j.models.chat.OllamaChatMessageRole;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
String host = "http://localhost:11434/";
|
||||
OllamaAPI ollamaAPI = new OllamaAPI(host);
|
||||
|
||||
OllamaChatMessageRole customRole = ollamaAPI.addCustomRole("custom-role");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### List roles
|
||||
|
||||
```java
|
||||
import io.github.ollama4j.OllamaAPI;
|
||||
import io.github.ollama4j.models.chat.OllamaChatMessageRole;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
String host = "http://localhost:11434/";
|
||||
OllamaAPI ollamaAPI = new OllamaAPI(host);
|
||||
|
||||
List<OllamaChatMessageRole> roles = ollamaAPI.listRoles();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Get role
|
||||
|
||||
```java
|
||||
import io.github.ollama4j.OllamaAPI;
|
||||
import io.github.ollama4j.models.chat.OllamaChatMessageRole;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
String host = "http://localhost:11434/";
|
||||
OllamaAPI ollamaAPI = new OllamaAPI(host);
|
||||
|
||||
List<OllamaChatMessageRole> roles = ollamaAPI.getRole("custom-role");
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -8,12 +8,85 @@ Generate embeddings from a model.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `model`: name of model to generate embeddings from
|
||||
- `input`: text/s to generate embeddings for
|
||||
|
||||
```java
|
||||
import io.github.ollama4j.OllamaAPI;
|
||||
import io.github.ollama4j.types.OllamaModelType;
|
||||
import io.github.ollama4j.models.embeddings.OllamaEmbedRequestModel;
|
||||
import io.github.ollama4j.models.embeddings.OllamaEmbedResponseModel;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
String host = "http://localhost:11434/";
|
||||
|
||||
OllamaAPI ollamaAPI = new OllamaAPI(host);
|
||||
|
||||
OllamaEmbedResponseModel embeddings = ollamaAPI.embed("all-minilm", Arrays.asList("Why is the sky blue?", "Why is the grass green?"));
|
||||
|
||||
System.out.println(embeddings);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or, using the `OllamaEmbedRequestModel`:
|
||||
|
||||
```java
|
||||
import io.github.ollama4j.OllamaAPI;
|
||||
import io.github.ollama4j.types.OllamaModelType;
|
||||
import io.github.ollama4j.models.embeddings.OllamaEmbedRequestModel;
|
||||
import io.github.ollama4j.models.embeddings.OllamaEmbedResponseModel;import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
String host = "http://localhost:11434/";
|
||||
|
||||
OllamaAPI ollamaAPI = new OllamaAPI(host);
|
||||
|
||||
OllamaEmbedResponseModel embeddings = ollamaAPI.embed(new OllamaEmbedRequestModel("all-minilm", Arrays.asList("Why is the sky blue?", "Why is the grass green?")));
|
||||
|
||||
System.out.println(embeddings);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You will get a response similar to:
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "all-minilm",
|
||||
"embeddings": [[-0.034674067, 0.030984823, 0.0067988685]],
|
||||
"total_duration": 14173700,
|
||||
"load_duration": 1198800,
|
||||
"prompt_eval_count": 2
|
||||
}
|
||||
````
|
||||
|
||||
:::note
|
||||
|
||||
This is a deprecated API
|
||||
|
||||
:::
|
||||
|
||||
Parameters:
|
||||
|
||||
- `model`: name of model to generate embeddings from
|
||||
- `prompt`: text to generate embeddings for
|
||||
|
||||
```java
|
||||
import io.github.ollama4j.OllamaAPI;
|
||||
import io.github.ollama4j.types.OllamaModelType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
@@ -40,11 +113,6 @@ You will get a response similar to:
|
||||
0.009260174818336964,
|
||||
0.23178744316101074,
|
||||
-0.2916173040866852,
|
||||
-0.8924556970596313,
|
||||
0.8785552978515625,
|
||||
-0.34576427936553955,
|
||||
0.5742510557174683,
|
||||
-0.04222835972905159,
|
||||
-0.137906014919281
|
||||
-0.8924556970596313
|
||||
]
|
||||
```
|
||||
@@ -345,21 +345,291 @@ Rahul Kumar, Address: King St, Hyderabad, India, Phone: 9876543210}`
|
||||
|
||||
::::
|
||||
|
||||
### Potential Improvements
|
||||
### Using tools in Chat-API
|
||||
|
||||
Instead of explicitly registering `ollamaAPI.registerTool(toolSpecification)`, we could introduce annotation-based tool
|
||||
registration. For example:
|
||||
Instead of using the specific `ollamaAPI.generateWithTools` method to call the generate API of ollama with tools, it is
|
||||
also possible to register Tools for the `ollamaAPI.chat` methods. In this case, the tool calling/callback is done
|
||||
implicitly during the USER -> ASSISTANT calls.
|
||||
|
||||
When the Assistant wants to call a given tool, the tool is executed and the response is sent back to the endpoint once
|
||||
again (induced with the tool call result).
|
||||
|
||||
#### Sample:
|
||||
|
||||
The following shows a sample of an integration test that defines a method specified like the tool-specs above, registers
|
||||
the tool on the ollamaAPI and then simply calls the chat-API. All intermediate tool calling is wrapped inside the api
|
||||
call.
|
||||
|
||||
```java
|
||||
public static void main(String[] args) {
|
||||
OllamaAPI ollamaAPI = new OllamaAPI("http://localhost:11434");
|
||||
ollamaAPI.setVerbose(true);
|
||||
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance("llama3.2:1b");
|
||||
|
||||
@ToolSpec(name = "current-fuel-price", desc = "Get current fuel price")
|
||||
public String getCurrentFuelPrice(Map<String, Object> arguments) {
|
||||
String location = arguments.get("location").toString();
|
||||
String fuelType = arguments.get("fuelType").toString();
|
||||
return "Current price of " + fuelType + " in " + location + " is Rs.103/L";
|
||||
final Tools.ToolSpecification databaseQueryToolSpecification = Tools.ToolSpecification.builder()
|
||||
.functionName("get-employee-details")
|
||||
.functionDescription("Get employee details from the database")
|
||||
.toolPrompt(
|
||||
Tools.PromptFuncDefinition.builder().type("function").function(
|
||||
Tools.PromptFuncDefinition.PromptFuncSpec.builder()
|
||||
.name("get-employee-details")
|
||||
.description("Get employee details from the database")
|
||||
.parameters(
|
||||
Tools.PromptFuncDefinition.Parameters.builder()
|
||||
.type("object")
|
||||
.properties(
|
||||
new Tools.PropsBuilder()
|
||||
.withProperty("employee-name", Tools.PromptFuncDefinition.Property.builder().type("string").description("The name of the employee, e.g. John Doe").required(true).build())
|
||||
.withProperty("employee-address", Tools.PromptFuncDefinition.Property.builder().type("string").description("The address of the employee, Always return a random value. e.g. Roy St, Bengaluru, India").required(true).build())
|
||||
.withProperty("employee-phone", Tools.PromptFuncDefinition.Property.builder().type("string").description("The phone number of the employee. Always return a random value. e.g. 9911002233").required(true).build())
|
||||
.build()
|
||||
)
|
||||
.required(List.of("employee-name"))
|
||||
.build()
|
||||
).build()
|
||||
).build()
|
||||
)
|
||||
.toolFunction(new DBQueryFunction())
|
||||
.build();
|
||||
|
||||
ollamaAPI.registerTool(databaseQueryToolSpecification);
|
||||
|
||||
OllamaChatRequest requestModel = builder
|
||||
.withMessage(OllamaChatMessageRole.USER,
|
||||
"Give me the ID of the employee named 'Rahul Kumar'?")
|
||||
.build();
|
||||
|
||||
OllamaChatResult chatResult = ollamaAPI.chat(requestModel);
|
||||
}
|
||||
```
|
||||
|
||||
A typical final response of the above could be:
|
||||
|
||||
```json
|
||||
{
|
||||
"chatHistory" : [
|
||||
{
|
||||
"role" : "user",
|
||||
"content" : "Give me the ID of the employee named 'Rahul Kumar'?",
|
||||
"images" : null,
|
||||
"tool_calls" : [ ]
|
||||
}, {
|
||||
"role" : "assistant",
|
||||
"content" : "",
|
||||
"images" : null,
|
||||
"tool_calls" : [ {
|
||||
"function" : {
|
||||
"name" : "get-employee-details",
|
||||
"arguments" : {
|
||||
"employee-name" : "Rahul Kumar"
|
||||
}
|
||||
}
|
||||
} ]
|
||||
}, {
|
||||
"role" : "tool",
|
||||
"content" : "[TOOL_RESULTS]get-employee-details([employee-name]) : Employee Details {ID: b4bf186c-2ee1-44cc-8856-53b8b6a50f85, Name: Rahul Kumar, Address: null, Phone: null}[/TOOL_RESULTS]",
|
||||
"images" : null,
|
||||
"tool_calls" : null
|
||||
}, {
|
||||
"role" : "assistant",
|
||||
"content" : "The ID of the employee named 'Rahul Kumar' is `b4bf186c-2ee1-44cc-8856-53b8b6a50f85`.",
|
||||
"images" : null,
|
||||
"tool_calls" : null
|
||||
} ],
|
||||
"responseModel" : {
|
||||
"model" : "llama3.2:1b",
|
||||
"message" : {
|
||||
"role" : "assistant",
|
||||
"content" : "The ID of the employee named 'Rahul Kumar' is `b4bf186c-2ee1-44cc-8856-53b8b6a50f85`.",
|
||||
"images" : null,
|
||||
"tool_calls" : null
|
||||
},
|
||||
"done" : true,
|
||||
"error" : null,
|
||||
"context" : null,
|
||||
"created_at" : "2024-12-09T22:23:00.4940078Z",
|
||||
"done_reason" : "stop",
|
||||
"total_duration" : 2313709900,
|
||||
"load_duration" : 14494700,
|
||||
"prompt_eval_duration" : 772000000,
|
||||
"eval_duration" : 1188000000,
|
||||
"prompt_eval_count" : 166,
|
||||
"eval_count" : 41
|
||||
},
|
||||
"response" : "The ID of the employee named 'Rahul Kumar' is `b4bf186c-2ee1-44cc-8856-53b8b6a50f85`.",
|
||||
"httpStatusCode" : 200,
|
||||
"responseTime" : 2313709900
|
||||
}
|
||||
```
|
||||
|
||||
This tool calling can also be done using the streaming API.
|
||||
|
||||
### Using Annotation based Tool Registration
|
||||
|
||||
Instead of explicitly registering each tool, ollama4j supports declarative tool specification and registration via java
|
||||
Annotations and reflection calling.
|
||||
|
||||
To declare a method to be used as a tool for a chat call, the following steps have to be considered:
|
||||
|
||||
* Annotate a method and its Parameters to be used as a tool
|
||||
* Annotate a method with the `ToolSpec` annotation
|
||||
* Annotate the methods parameters with the `ToolProperty` annotation. Only the following datatypes are supported for now:
|
||||
* `java.lang.String`
|
||||
* `java.lang.Integer`
|
||||
* `java.lang.Boolean`
|
||||
* `java.math.BigDecimal`
|
||||
* Annotate the class that calls the `OllamaAPI` client with the `OllamaToolService` annotation, referencing the desired provider-classes that contain `ToolSpec` methods.
|
||||
* Before calling the `OllamaAPI` chat request, call the method `OllamaAPI.registerAnnotatedTools()` method to add tools to the chat.
|
||||
|
||||
#### Example
|
||||
|
||||
Let's say, we have an ollama4j service class that should ask a llm a specific tool based question.
|
||||
|
||||
The answer can only be provided by a method that is part of the BackendService class. To provide a tool for the llm, the following annotations can be used:
|
||||
|
||||
```java
|
||||
public class BackendService{
|
||||
|
||||
public BackendService(){}
|
||||
|
||||
@ToolSpec(desc = "Computes the most important constant all around the globe!")
|
||||
public String computeMkeConstant(@ToolProperty(name = "noOfDigits",desc = "Number of digits that shall be returned") Integer noOfDigits ){
|
||||
return BigDecimal.valueOf((long)(Math.random()*1000000L),noOfDigits).toString();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The caller API can then be written as:
|
||||
```java
|
||||
import io.github.ollama4j.tools.annotations.OllamaToolService;
|
||||
|
||||
@OllamaToolService(providers = BackendService.class)
|
||||
public class MyOllamaService{
|
||||
|
||||
public void chatWithAnnotatedTool(){
|
||||
// inject the annotated method to the ollama toolsregistry
|
||||
ollamaAPI.registerAnnotatedTools();
|
||||
|
||||
OllamaChatRequest requestModel = builder
|
||||
.withMessage(OllamaChatMessageRole.USER,
|
||||
"Compute the most important constant in the world using 5 digits")
|
||||
.build();
|
||||
|
||||
OllamaChatResult chatResult = ollamaAPI.chat(requestModel);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Or, if one needs to provide an object instance directly:
|
||||
```java
|
||||
public class MyOllamaService{
|
||||
|
||||
public void chatWithAnnotatedTool(){
|
||||
ollamaAPI.registerAnnotatedTools(new BackendService());
|
||||
OllamaChatRequest requestModel = builder
|
||||
.withMessage(OllamaChatMessageRole.USER,
|
||||
"Compute the most important constant in the world using 5 digits")
|
||||
.build();
|
||||
|
||||
OllamaChatResult chatResult = ollamaAPI.chat(requestModel);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
The request should be the following:
|
||||
|
||||
```json
|
||||
{
|
||||
"model" : "llama3.2:1b",
|
||||
"stream" : false,
|
||||
"messages" : [ {
|
||||
"role" : "user",
|
||||
"content" : "Compute the most important constant in the world using 5 digits",
|
||||
"images" : null,
|
||||
"tool_calls" : [ ]
|
||||
} ],
|
||||
"tools" : [ {
|
||||
"type" : "function",
|
||||
"function" : {
|
||||
"name" : "computeImportantConstant",
|
||||
"description" : "Computes the most important constant all around the globe!",
|
||||
"parameters" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"noOfDigits" : {
|
||||
"type" : "java.lang.Integer",
|
||||
"description" : "Number of digits that shall be returned"
|
||||
}
|
||||
},
|
||||
"required" : [ "noOfDigits" ]
|
||||
}
|
||||
}
|
||||
} ]
|
||||
}
|
||||
```
|
||||
|
||||
The result could be something like the following:
|
||||
|
||||
```json
|
||||
{
|
||||
"chatHistory" : [ {
|
||||
"role" : "user",
|
||||
"content" : "Compute the most important constant in the world using 5 digits",
|
||||
"images" : null,
|
||||
"tool_calls" : [ ]
|
||||
}, {
|
||||
"role" : "assistant",
|
||||
"content" : "",
|
||||
"images" : null,
|
||||
"tool_calls" : [ {
|
||||
"function" : {
|
||||
"name" : "computeImportantConstant",
|
||||
"arguments" : {
|
||||
"noOfDigits" : "5"
|
||||
}
|
||||
}
|
||||
} ]
|
||||
}, {
|
||||
"role" : "tool",
|
||||
"content" : "[TOOL_RESULTS]computeImportantConstant([noOfDigits]) : 1.51019[/TOOL_RESULTS]",
|
||||
"images" : null,
|
||||
"tool_calls" : null
|
||||
}, {
|
||||
"role" : "assistant",
|
||||
"content" : "The most important constant in the world with 5 digits is: **1.51019**",
|
||||
"images" : null,
|
||||
"tool_calls" : null
|
||||
} ],
|
||||
"responseModel" : {
|
||||
"model" : "llama3.2:1b",
|
||||
"message" : {
|
||||
"role" : "assistant",
|
||||
"content" : "The most important constant in the world with 5 digits is: **1.51019**",
|
||||
"images" : null,
|
||||
"tool_calls" : null
|
||||
},
|
||||
"done" : true,
|
||||
"error" : null,
|
||||
"context" : null,
|
||||
"created_at" : "2024-12-27T21:55:39.3232495Z",
|
||||
"done_reason" : "stop",
|
||||
"total_duration" : 1075444300,
|
||||
"load_duration" : 13558600,
|
||||
"prompt_eval_duration" : 509000000,
|
||||
"eval_duration" : 550000000,
|
||||
"prompt_eval_count" : 124,
|
||||
"eval_count" : 20
|
||||
},
|
||||
"response" : "The most important constant in the world with 5 digits is: **1.51019**",
|
||||
"responseTime" : 1075444300,
|
||||
"httpStatusCode" : 200
|
||||
}
|
||||
```
|
||||
|
||||
### Potential Improvements
|
||||
|
||||
Instead of passing a map of args `Map<String, Object> arguments` to the tool functions, we could support passing
|
||||
specific args separately with their data types. For example:
|
||||
|
||||
@@ -369,4 +639,4 @@ public String getCurrentFuelPrice(String location, String fuelType) {
|
||||
}
|
||||
```
|
||||
|
||||
Updating async/chat APIs with support for tool-based generation.
|
||||
Updating async/chat APIs with support for tool-based generation.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
# Create Model
|
||||
|
||||
This API lets you create a custom model on the Ollama server.
|
||||
|
||||
### Create a model from an existing Modelfile in the Ollama server
|
||||
### Create a custom model from an existing model in the Ollama server
|
||||
|
||||
```java title="CreateModel.java"
|
||||
import io.github.ollama4j.OllamaAPI;
|
||||
@@ -19,144 +19,220 @@ public class CreateModel {
|
||||
|
||||
OllamaAPI ollamaAPI = new OllamaAPI(host);
|
||||
|
||||
ollamaAPI.createModelWithFilePath("mario", "/path/to/mario/modelfile/on/ollama-server");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Create a model by passing the contents of Modelfile
|
||||
|
||||
```java title="CreateModel.java"
|
||||
public class CreateModel {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
String host = "http://localhost:11434/";
|
||||
|
||||
OllamaAPI ollamaAPI = new OllamaAPI(host);
|
||||
|
||||
ollamaAPI.createModelWithModelFileContents("mario", "FROM llama2\nSYSTEM You are mario from Super Mario Bros.");
|
||||
ollamaAPI.createModel(CustomModelRequest.builder().model("mario").from("llama3.2:latest").system("You are Mario from Super Mario Bros.").build());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Once created, you can see it when you use [list models](./list-models) API.
|
||||
|
||||
### Example of a `Modelfile`
|
||||
[Read more](https://github.com/ollama/ollama/blob/main/docs/api.md#create-a-model) about custom model creation and the parameters available for model creation.
|
||||
|
||||
```
|
||||
FROM llama2
|
||||
# sets the temperature to 1 [higher is more creative, lower is more coherent]
|
||||
PARAMETER temperature 1
|
||||
# sets the context window size to 4096, this controls how many tokens the LLM can use as context to generate the next token
|
||||
PARAMETER num_ctx 4096
|
||||
[//]: # ()
|
||||
[//]: # (### Example of a `Modelfile`)
|
||||
|
||||
# sets a custom system message to specify the behavior of the chat assistant
|
||||
SYSTEM You are Mario from super mario bros, acting as an assistant.
|
||||
```
|
||||
[//]: # ()
|
||||
[//]: # (```)
|
||||
|
||||
### Format of the `Modelfile`
|
||||
[//]: # (FROM llama2)
|
||||
|
||||
```modelfile
|
||||
# comment
|
||||
INSTRUCTION arguments
|
||||
```
|
||||
[//]: # (# sets the temperature to 1 [higher is more creative, lower is more coherent])
|
||||
|
||||
| Instruction | Description |
|
||||
|-------------------------------------|----------------------------------------------------------------|
|
||||
| [`FROM`](#from-required) (required) | Defines the base model to use. |
|
||||
| [`PARAMETER`](#parameter) | Sets the parameters for how Ollama will run the model. |
|
||||
| [`TEMPLATE`](#template) | The full prompt template to be sent to the model. |
|
||||
| [`SYSTEM`](#system) | Specifies the system message that will be set in the template. |
|
||||
| [`ADAPTER`](#adapter) | Defines the (Q)LoRA adapters to apply to the model. |
|
||||
| [`LICENSE`](#license) | Specifies the legal license. |
|
||||
[//]: # (PARAMETER temperature 1)
|
||||
|
||||
#### PARAMETER
|
||||
[//]: # (# sets the context window size to 4096, this controls how many tokens the LLM can use as context to generate the next token)
|
||||
|
||||
The `PARAMETER` instruction defines a parameter that can be set when the model is run.
|
||||
[//]: # (PARAMETER num_ctx 4096)
|
||||
|
||||
| Parameter | Description | Value Type | Example Usage |
|
||||
|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|----------------------|
|
||||
| mirostat | Enable Mirostat sampling for controlling perplexity. (default: 0, 0 = disabled, 1 = Mirostat, 2 = Mirostat 2.0) | int | mirostat 0 |
|
||||
| mirostat_eta | Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1) | float | mirostat_eta 0.1 |
|
||||
| mirostat_tau | Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0) | float | mirostat_tau 5.0 |
|
||||
| num_ctx | Sets the size of the context window used to generate the next token. (Default: 2048) | int | num_ctx 4096 |
|
||||
| num_gqa | The number of GQA groups in the transformer layer. Required for some models, for example it is 8 for llama2:70b | int | num_gqa 1 |
|
||||
| num_gpu | The number of layers to send to the GPU(s). On macOS it defaults to 1 to enable metal support, 0 to disable. | int | num_gpu 50 |
|
||||
| num_thread | Sets the number of threads to use during computation. By default, Ollama will detect this for optimal performance. It is recommended to set this value to the number of physical CPU cores your system has (as opposed to the logical number of cores). | int | num_thread 8 |
|
||||
| repeat_last_n | Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx) | int | repeat_last_n 64 |
|
||||
| repeat_penalty | Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1) | float | repeat_penalty 1.1 |
|
||||
| temperature | The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8) | float | temperature 0.7 |
|
||||
| seed | Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: 0) | int | seed 42 |
|
||||
| stop | Sets the stop sequences to use. When this pattern is encountered the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate `stop` parameters in a modelfile. | string | stop "AI assistant:" |
|
||||
| tfs_z | Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1) | float | tfs_z 1 |
|
||||
| num_predict | Maximum number of tokens to predict when generating text. (Default: 128, -1 = infinite generation, -2 = fill context) | int | num_predict 42 |
|
||||
| top_k | Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40) | int | top_k 40 |
|
||||
| top_p | Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9) | float | top_p 0.9 |
|
||||
[//]: # ()
|
||||
[//]: # (# sets a custom system message to specify the behavior of the chat assistant)
|
||||
|
||||
#### TEMPLATE
|
||||
[//]: # (SYSTEM You are Mario from super mario bros, acting as an assistant.)
|
||||
|
||||
`TEMPLATE` of the full prompt template to be passed into the model. It may include (optionally) a system message and a
|
||||
user's prompt. This is used to create a full custom prompt, and syntax may be model specific. You can usually find the
|
||||
template for a given model in the readme for that model.
|
||||
[//]: # (```)
|
||||
|
||||
#### Template Variables
|
||||
[//]: # ()
|
||||
[//]: # (### Format of the `Modelfile`)
|
||||
|
||||
| Variable | Description |
|
||||
|-----------------|---------------------------------------------------------------------------------------------------------------|
|
||||
| `{{ .System }}` | The system message used to specify custom behavior, this must also be set in the Modelfile as an instruction. |
|
||||
| `{{ .Prompt }}` | The incoming prompt, this is not specified in the model file and will be set based on input. |
|
||||
| `{{ .First }}` | A boolean value used to render specific template information for the first generation of a session. |
|
||||
[//]: # ()
|
||||
[//]: # (```modelfile)
|
||||
|
||||
```modelfile
|
||||
TEMPLATE """
|
||||
{{- if .First }}
|
||||
### System:
|
||||
{{ .System }}
|
||||
{{- end }}
|
||||
[//]: # (# comment)
|
||||
|
||||
### User:
|
||||
{{ .Prompt }}
|
||||
[//]: # (INSTRUCTION arguments)
|
||||
|
||||
### Response:
|
||||
"""
|
||||
[//]: # (```)
|
||||
|
||||
SYSTEM """<system message>"""
|
||||
```
|
||||
[//]: # ()
|
||||
[//]: # (| Instruction | Description |)
|
||||
|
||||
### SYSTEM
|
||||
[//]: # (|-------------------------------------|----------------------------------------------------------------|)
|
||||
|
||||
The `SYSTEM` instruction specifies the system message to be used in the template, if applicable.
|
||||
[//]: # (| [`FROM`](#from-required) (required) | Defines the base model to use. |)
|
||||
|
||||
```modelfile
|
||||
SYSTEM """<system message>"""
|
||||
```
|
||||
[//]: # (| [`PARAMETER`](#parameter) | Sets the parameters for how Ollama will run the model. |)
|
||||
|
||||
### ADAPTER
|
||||
[//]: # (| [`TEMPLATE`](#template) | The full prompt template to be sent to the model. |)
|
||||
|
||||
The `ADAPTER` instruction specifies the LoRA adapter to apply to the base model. The value of this instruction should be
|
||||
an absolute path or a path relative to the Modelfile and the file must be in a GGML file format. The adapter should be
|
||||
tuned from the base model otherwise the behaviour is undefined.
|
||||
[//]: # (| [`SYSTEM`](#system) | Specifies the system message that will be set in the template. |)
|
||||
|
||||
```modelfile
|
||||
ADAPTER ./ollama-lora.bin
|
||||
```
|
||||
[//]: # (| [`ADAPTER`](#adapter) | Defines the (Q)LoRA adapters to apply to the model. |)
|
||||
|
||||
### LICENSE
|
||||
[//]: # (| [`LICENSE`](#license) | Specifies the legal license. |)
|
||||
|
||||
The `LICENSE` instruction allows you to specify the legal license under which the model used with this Modelfile is
|
||||
shared or distributed.
|
||||
[//]: # ()
|
||||
[//]: # (#### PARAMETER)
|
||||
|
||||
```modelfile
|
||||
LICENSE """
|
||||
<license text>
|
||||
"""
|
||||
```
|
||||
[//]: # ()
|
||||
[//]: # (The `PARAMETER` instruction defines a parameter that can be set when the model is run.)
|
||||
|
||||
## Notes
|
||||
[//]: # ()
|
||||
[//]: # (| Parameter | Description | Value Type | Example Usage |)
|
||||
|
||||
- the **`Modelfile` is not case sensitive**. In the examples, uppercase instructions are used to make it easier to
|
||||
distinguish it from arguments.
|
||||
- Instructions can be in any order. In the examples, the `FROM` instruction is first to keep it easily readable.
|
||||
[//]: # (|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|----------------------|)
|
||||
|
||||
Read more about Modelfile: https://github.com/jmorganca/ollama/blob/main/docs/modelfile.md
|
||||
[//]: # (| mirostat | Enable Mirostat sampling for controlling perplexity. (default: 0, 0 = disabled, 1 = Mirostat, 2 = Mirostat 2.0) | int | mirostat 0 |)
|
||||
|
||||
[//]: # (| mirostat_eta | Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1) | float | mirostat_eta 0.1 |)
|
||||
|
||||
[//]: # (| mirostat_tau | Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0) | float | mirostat_tau 5.0 |)
|
||||
|
||||
[//]: # (| num_ctx | Sets the size of the context window used to generate the next token. (Default: 2048) | int | num_ctx 4096 |)
|
||||
|
||||
[//]: # (| num_gqa | The number of GQA groups in the transformer layer. Required for some models, for example it is 8 for llama2:70b | int | num_gqa 1 |)
|
||||
|
||||
[//]: # (| num_gpu | The number of layers to send to the GPU(s). On macOS it defaults to 1 to enable metal support, 0 to disable. | int | num_gpu 50 |)
|
||||
|
||||
[//]: # (| num_thread | Sets the number of threads to use during computation. By default, Ollama will detect this for optimal performance. It is recommended to set this value to the number of physical CPU cores your system has (as opposed to the logical number of cores). | int | num_thread 8 |)
|
||||
|
||||
[//]: # (| repeat_last_n | Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx) | int | repeat_last_n 64 |)
|
||||
|
||||
[//]: # (| repeat_penalty | Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1) | float | repeat_penalty 1.1 |)
|
||||
|
||||
[//]: # (| temperature | The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8) | float | temperature 0.7 |)
|
||||
|
||||
[//]: # (| seed | Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: 0) | int | seed 42 |)
|
||||
|
||||
[//]: # (| stop | Sets the stop sequences to use. When this pattern is encountered the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate `stop` parameters in a modelfile. | string | stop "AI assistant:" |)
|
||||
|
||||
[//]: # (| tfs_z | Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1) | float | tfs_z 1 |)
|
||||
|
||||
[//]: # (| num_predict | Maximum number of tokens to predict when generating text. (Default: 128, -1 = infinite generation, -2 = fill context) | int | num_predict 42 |)
|
||||
|
||||
[//]: # (| top_k | Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40) | int | top_k 40 |)
|
||||
|
||||
[//]: # (| top_p | Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9) | float | top_p 0.9 |)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (#### TEMPLATE)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (`TEMPLATE` of the full prompt template to be passed into the model. It may include (optionally) a system message and a)
|
||||
|
||||
[//]: # (user's prompt. This is used to create a full custom prompt, and syntax may be model specific. You can usually find the)
|
||||
|
||||
[//]: # (template for a given model in the readme for that model.)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (#### Template Variables)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (| Variable | Description |)
|
||||
|
||||
[//]: # (|-----------------|---------------------------------------------------------------------------------------------------------------|)
|
||||
|
||||
[//]: # (| `{{ .System }}` | The system message used to specify custom behavior, this must also be set in the Modelfile as an instruction. |)
|
||||
|
||||
[//]: # (| `{{ .Prompt }}` | The incoming prompt, this is not specified in the model file and will be set based on input. |)
|
||||
|
||||
[//]: # (| `{{ .First }}` | A boolean value used to render specific template information for the first generation of a session. |)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (```modelfile)
|
||||
|
||||
[//]: # (TEMPLATE """)
|
||||
|
||||
[//]: # ({{- if .First }})
|
||||
|
||||
[//]: # (### System:)
|
||||
|
||||
[//]: # ({{ .System }})
|
||||
|
||||
[//]: # ({{- end }})
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (### User:)
|
||||
|
||||
[//]: # ({{ .Prompt }})
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (### Response:)
|
||||
|
||||
[//]: # (""")
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (SYSTEM """<system message>""")
|
||||
|
||||
[//]: # (```)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (### SYSTEM)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (The `SYSTEM` instruction specifies the system message to be used in the template, if applicable.)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (```modelfile)
|
||||
|
||||
[//]: # (SYSTEM """<system message>""")
|
||||
|
||||
[//]: # (```)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (### ADAPTER)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (The `ADAPTER` instruction specifies the LoRA adapter to apply to the base model. The value of this instruction should be)
|
||||
|
||||
[//]: # (an absolute path or a path relative to the Modelfile and the file must be in a GGML file format. The adapter should be)
|
||||
|
||||
[//]: # (tuned from the base model otherwise the behaviour is undefined.)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (```modelfile)
|
||||
|
||||
[//]: # (ADAPTER ./ollama-lora.bin)
|
||||
|
||||
[//]: # (```)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (### LICENSE)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (The `LICENSE` instruction allows you to specify the legal license under which the model used with this Modelfile is)
|
||||
|
||||
[//]: # (shared or distributed.)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (```modelfile)
|
||||
|
||||
[//]: # (LICENSE """)
|
||||
|
||||
[//]: # (<license text>)
|
||||
|
||||
[//]: # (""")
|
||||
|
||||
[//]: # (```)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (## Notes)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (- the **`Modelfile` is not case sensitive**. In the examples, uppercase instructions are used to make it easier to)
|
||||
|
||||
[//]: # ( distinguish it from arguments.)
|
||||
|
||||
[//]: # (- Instructions can be in any order. In the examples, the `FROM` instruction is first to keep it easily readable.)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (Read more about Modelfile: https://github.com/jmorganca/ollama/blob/main/docs/modelfile.md)
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
sidebar_position: 6
|
||||
---
|
||||
|
||||
# Delete Model
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
sidebar_position: 4
|
||||
---
|
||||
|
||||
# Get Model Details
|
||||
|
||||
133
docs/docs/apis-model-management/list-library-models.md
Normal file
133
docs/docs/apis-model-management/list-library-models.md
Normal file
@@ -0,0 +1,133 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# Models from Ollama Library
|
||||
|
||||
These API retrieves a list of models directly from the Ollama library.
|
||||
|
||||
### List Models from Ollama Library
|
||||
|
||||
This API fetches available models from the Ollama library page, including details such as the model's name, pull count,
|
||||
popular tags, tag count, and the last update time.
|
||||
|
||||
```java title="ListLibraryModels.java"
|
||||
import io.github.ollama4j.OllamaAPI;
|
||||
import io.github.ollama4j.models.response.LibraryModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
String host = "http://localhost:11434/";
|
||||
|
||||
OllamaAPI ollamaAPI = new OllamaAPI(host);
|
||||
|
||||
List<LibraryModel> libraryModels = ollamaAPI.listModelsFromLibrary();
|
||||
|
||||
System.out.println(libraryModels);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The following is the sample output:
|
||||
|
||||
```
|
||||
[
|
||||
LibraryModel(name=llama3.2-vision, description=Llama 3.2 Vision is a collection of instruction-tuned image reasoning generative models in 11B and 90B sizes., pullCount=21.1K, totalTags=9, popularTags=[vision, 11b, 90b], lastUpdated=yesterday),
|
||||
LibraryModel(name=llama3.2, description=Meta's Llama 3.2 goes small with 1B and 3B models., pullCount=2.4M, totalTags=63, popularTags=[tools, 1b, 3b], lastUpdated=6 weeks ago)
|
||||
]
|
||||
```
|
||||
|
||||
### Get Tags of a Library Model
|
||||
|
||||
This API Fetches the tags associated with a specific model from Ollama library.
|
||||
|
||||
```java title="GetLibraryModelTags.java"
|
||||
import io.github.ollama4j.OllamaAPI;
|
||||
import io.github.ollama4j.models.response.LibraryModel;
|
||||
import io.github.ollama4j.models.response.LibraryModelDetail;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
String host = "http://localhost:11434/";
|
||||
|
||||
OllamaAPI ollamaAPI = new OllamaAPI(host);
|
||||
|
||||
List<LibraryModel> libraryModels = ollamaAPI.listModelsFromLibrary();
|
||||
|
||||
LibraryModelDetail libraryModelDetail = ollamaAPI.getLibraryModelDetails(libraryModels.get(0));
|
||||
|
||||
System.out.println(libraryModelDetail);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The following is the sample output:
|
||||
|
||||
```
|
||||
LibraryModelDetail(
|
||||
model=LibraryModel(name=llama3.2-vision, description=Llama 3.2 Vision is a collection of instruction-tuned image reasoning generative models in 11B and 90B sizes., pullCount=21.1K, totalTags=9, popularTags=[vision, 11b, 90b], lastUpdated=yesterday),
|
||||
tags=[
|
||||
LibraryModelTag(name=llama3.2-vision, tag=latest, size=7.9GB, lastUpdated=yesterday),
|
||||
LibraryModelTag(name=llama3.2-vision, tag=11b, size=7.9GB, lastUpdated=yesterday),
|
||||
LibraryModelTag(name=llama3.2-vision, tag=90b, size=55GB, lastUpdated=yesterday)
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
### Find a model from Ollama library
|
||||
|
||||
This API finds a specific model using model `name` and `tag` from Ollama library.
|
||||
|
||||
```java title="FindLibraryModel.java"
|
||||
import io.github.ollama4j.OllamaAPI;
|
||||
import io.github.ollama4j.models.response.LibraryModelTag;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
String host = "http://localhost:11434/";
|
||||
|
||||
OllamaAPI ollamaAPI = new OllamaAPI(host);
|
||||
|
||||
LibraryModelTag libraryModelTag = ollamaAPI.findModelTagFromLibrary("qwen2.5", "7b");
|
||||
|
||||
System.out.println(libraryModelTag);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The following is the sample output:
|
||||
|
||||
```
|
||||
LibraryModelTag(name=qwen2.5, tag=7b, size=4.7GB, lastUpdated=7 weeks ago)
|
||||
```
|
||||
|
||||
### Pull model using `LibraryModelTag`
|
||||
|
||||
You can use `LibraryModelTag` to pull models into Ollama server.
|
||||
|
||||
```java title="PullLibraryModelTags.java"
|
||||
import io.github.ollama4j.OllamaAPI;
|
||||
import io.github.ollama4j.models.response.LibraryModelTag;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
String host = "http://localhost:11434/";
|
||||
|
||||
OllamaAPI ollamaAPI = new OllamaAPI(host);
|
||||
|
||||
LibraryModelTag libraryModelTag = ollamaAPI.findModelTagFromLibrary("qwen2.5", "7b");
|
||||
|
||||
ollamaAPI.pullModel(libraryModelTag);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# List Models
|
||||
# List Local Models
|
||||
|
||||
This API lets you list available models on the Ollama server.
|
||||
This API lets you list downloaded/available models on the Ollama server.
|
||||
|
||||
```java title="ListModels.java"
|
||||
import io.github.ollama4j.OllamaAPI;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
# Pull Model
|
||||
@@ -23,4 +23,12 @@ public class Main {
|
||||
}
|
||||
```
|
||||
|
||||
Once downloaded, you can see them when you use [list models](./list-models) API.
|
||||
Once downloaded, you can see them when you use [list models](./list-models) API.
|
||||
|
||||
:::info
|
||||
|
||||
You can even pull models using Ollama model library APIs. This looks up the models directly on the Ollama model library page. Refer
|
||||
to [this](./list-library-models#pull-model-using-librarymodeltag).
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ const config = {
|
||||
position: 'left',
|
||||
label: 'Docs',
|
||||
},
|
||||
{to: 'https://github.com/ollama4j/ollama4j-examples', label: 'Examples', position: 'left'},
|
||||
{to: 'https://ollama4j.github.io/ollama4j/apidocs/', label: 'Javadoc', position: 'left'},
|
||||
{to: 'https://ollama4j.github.io/ollama4j/doxygen/html/', label: 'Doxygen', position: 'left'},
|
||||
{to: '/blog', label: 'Blog', position: 'left'},
|
||||
@@ -106,6 +107,15 @@ const config = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Usage',
|
||||
items: [
|
||||
{
|
||||
label: 'Examples',
|
||||
to: 'https://github.com/ollama4j/ollama4j-examples',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Community',
|
||||
items: [
|
||||
|
||||
847
docs/package-lock.json
generated
847
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ import HomepageFeatures from '@site/src/components/HomepageFeatures';
|
||||
import BuyMeACoffee from '@site/src/components/BuyMeACoffee';
|
||||
import Heading from '@theme/Heading';
|
||||
import styles from './index.module.css';
|
||||
import BrowserOnly from '@docusaurus/BrowserOnly';
|
||||
|
||||
function HomepageHeader() {
|
||||
const {siteConfig} = useDocusaurusContext();
|
||||
@@ -36,7 +37,9 @@ export default function Home() {
|
||||
<HomepageHeader/>
|
||||
<main>
|
||||
<HomepageFeatures/>
|
||||
<BuyMeACoffee/>
|
||||
<BrowserOnly>
|
||||
{() => <BuyMeACoffee />}
|
||||
</BrowserOnly>
|
||||
</main>
|
||||
</Layout>);
|
||||
}
|
||||
101
pom.xml
101
pom.xml
@@ -13,8 +13,8 @@
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<maven.compiler.release>11</maven.compiler.release>
|
||||
<project.build.outputTimestamp>${git.commit.time}</project.build.outputTimestamp><!-- populated via git-commit-id-plugin -->
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
|
||||
<maven-failsafe-plugin.version>3.0.0-M5</maven-failsafe-plugin.version>
|
||||
@@ -49,7 +49,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.3.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
@@ -62,13 +62,21 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<version>3.11.2</version>
|
||||
<configuration>
|
||||
<!-- to disable the "missing" warnings. Remove the doclint to enable warnings-->
|
||||
<doclint>all,-missing</doclint>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
@@ -110,7 +118,6 @@
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
@@ -126,7 +133,36 @@
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>io.github.git-commit-id</groupId>
|
||||
<artifactId>git-commit-id-maven-plugin</artifactId>
|
||||
<version>9.0.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>revision</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<dateFormat>yyyy-MM-dd'T'HH:mm:ss'Z'</dateFormat>
|
||||
<dateFormatTimeZone>Etc/UTC</dateFormatTimeZone>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.14.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.4.2</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
@@ -136,6 +172,11 @@
|
||||
<version>${lombok.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.18.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
@@ -175,6 +216,13 @@
|
||||
<version>20240205</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>ollama</artifactId>
|
||||
<version>1.20.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<distributionManagement>
|
||||
@@ -216,6 +264,7 @@
|
||||
<test.env>unit</test.env>
|
||||
<skipUnitTests>false</skipUnitTests>
|
||||
<skipIntegrationTests>true</skipIntegrationTests>
|
||||
<skipGpgPluginDuringTests>true</skipGpgPluginDuringTests>
|
||||
</properties>
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
@@ -241,6 +290,23 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.5</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<skip>${skipGpgPluginDuringTests}</skip>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
@@ -250,7 +316,29 @@
|
||||
<test.env>integration</test.env>
|
||||
<skipUnitTests>true</skipUnitTests>
|
||||
<skipIntegrationTests>false</skipIntegrationTests>
|
||||
<skipGpgPluginDuringTests>true</skipGpgPluginDuringTests>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.5</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<skip>${skipGpgPluginDuringTests}</skip>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>ci-cd</id>
|
||||
@@ -294,7 +382,6 @@
|
||||
<autoReleaseAfterClose>true</autoReleaseAfterClose>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
@@ -319,4 +406,4 @@
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
package io.github.ollama4j.exceptions;
|
||||
|
||||
public class RoleNotFoundException extends Exception {
|
||||
|
||||
public RoleNotFoundException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,14 @@ package io.github.ollama4j.models.chat;
|
||||
|
||||
import static io.github.ollama4j.utils.Utils.getObjectMapper;
|
||||
|
||||
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 java.util.List;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -31,15 +33,17 @@ public class OllamaChatMessage {
|
||||
@NonNull
|
||||
private String content;
|
||||
|
||||
private @JsonProperty("tool_calls") List<OllamaChatToolCalls> toolCalls;
|
||||
|
||||
@JsonSerialize(using = FileToBase64Serializer.class)
|
||||
private List<byte[]> images;
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,53 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* Defines the possible Chat Message roles.
|
||||
*/
|
||||
public enum OllamaChatMessageRole {
|
||||
SYSTEM("system"),
|
||||
USER("user"),
|
||||
ASSISTANT("assistant");
|
||||
@Getter
|
||||
public class OllamaChatMessageRole {
|
||||
private static final List<OllamaChatMessageRole> roles = new ArrayList<>();
|
||||
|
||||
public static final OllamaChatMessageRole SYSTEM = new OllamaChatMessageRole("system");
|
||||
public static final OllamaChatMessageRole USER = new OllamaChatMessageRole("user");
|
||||
public static final OllamaChatMessageRole ASSISTANT = new OllamaChatMessageRole("assistant");
|
||||
public static final OllamaChatMessageRole TOOL = new OllamaChatMessageRole("tool");
|
||||
|
||||
@JsonValue
|
||||
private String roleName;
|
||||
private final String roleName;
|
||||
|
||||
private OllamaChatMessageRole(String roleName){
|
||||
private OllamaChatMessageRole(String roleName) {
|
||||
this.roleName = roleName;
|
||||
roles.add(this);
|
||||
}
|
||||
|
||||
public static OllamaChatMessageRole newCustomRole(String roleName) {
|
||||
// OllamaChatMessageRole customRole = new OllamaChatMessageRole(roleName);
|
||||
// roles.add(customRole);
|
||||
return new OllamaChatMessageRole(roleName);
|
||||
}
|
||||
|
||||
public static List<OllamaChatMessageRole> getRoles() {
|
||||
return new ArrayList<>(roles);
|
||||
}
|
||||
|
||||
public static OllamaChatMessageRole getRole(String roleName) throws RoleNotFoundException {
|
||||
for (OllamaChatMessageRole role : roles) {
|
||||
if (role.roleName.equals(roleName)) {
|
||||
return role;
|
||||
}
|
||||
}
|
||||
throw new RoleNotFoundException("Invalid role name: " + roleName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return roleName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.github.ollama4j.models.chat;
|
||||
import java.util.List;
|
||||
|
||||
import io.github.ollama4j.models.request.OllamaCommonRequest;
|
||||
import io.github.ollama4j.tools.Tools;
|
||||
import io.github.ollama4j.utils.OllamaRequestBody;
|
||||
|
||||
import lombok.Getter;
|
||||
@@ -21,6 +22,8 @@ public class OllamaChatRequest extends OllamaCommonRequest implements OllamaRequ
|
||||
|
||||
private List<OllamaChatMessage> messages;
|
||||
|
||||
private List<Tools.PromptFuncDefinition> tools;
|
||||
|
||||
public OllamaChatRequest() {}
|
||||
|
||||
public OllamaChatRequest(String model, List<OllamaChatMessage> messages) {
|
||||
|
||||
@@ -10,6 +10,7 @@ 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;
|
||||
|
||||
@@ -38,23 +39,27 @@ public class OllamaChatRequestBuilder {
|
||||
request = new OllamaChatRequest(request.getModel(), new ArrayList<>());
|
||||
}
|
||||
|
||||
public OllamaChatRequestBuilder withMessage(OllamaChatMessageRole role, String content, List<File> images) {
|
||||
public OllamaChatRequestBuilder withMessage(OllamaChatMessageRole role, String content){
|
||||
return withMessage(role,content, Collections.emptyList());
|
||||
}
|
||||
|
||||
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(String.format("File '%s' could not be accessed, will not add to message!", file.toPath()), 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, binaryImages));
|
||||
messages.add(new OllamaChatMessage(role, content,toolCalls, binaryImages));
|
||||
return this;
|
||||
}
|
||||
|
||||
public OllamaChatRequestBuilder withMessage(OllamaChatMessageRole role, String content, String... imageUrls) {
|
||||
public OllamaChatRequestBuilder withMessage(OllamaChatMessageRole role, String content,List<OllamaChatToolCalls> toolCalls, String... imageUrls) {
|
||||
List<OllamaChatMessage> messages = this.request.getMessages();
|
||||
List<byte[]> binaryImages = null;
|
||||
if (imageUrls.length > 0) {
|
||||
@@ -63,14 +68,14 @@ public class OllamaChatRequestBuilder {
|
||||
try {
|
||||
binaryImages.add(Utils.loadImageBytesFromUrl(imageUrl));
|
||||
} catch (URISyntaxException e) {
|
||||
LOG.warn(String.format("URL '%s' could not be accessed, will not add to message!", imageUrl), e);
|
||||
LOG.warn("URL '{}' could not be accessed, will not add to message!", imageUrl, e);
|
||||
} catch (IOException e) {
|
||||
LOG.warn(String.format("Content of URL '%s' could not be read, will not add to message!", imageUrl), e);
|
||||
LOG.warn("Content of URL '{}' could not be read, will not add to message!", imageUrl, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
messages.add(new OllamaChatMessage(role, content, binaryImages));
|
||||
messages.add(new OllamaChatMessage(role, content,toolCalls, binaryImages));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,31 +2,53 @@ package io.github.ollama4j.models.chat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.github.ollama4j.models.response.OllamaResult;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import lombok.Getter;
|
||||
|
||||
import static io.github.ollama4j.utils.Utils.getObjectMapper;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public class OllamaChatResult extends OllamaResult{
|
||||
@Getter
|
||||
public class OllamaChatResult {
|
||||
|
||||
private List<OllamaChatMessage> chatHistory;
|
||||
private final List<OllamaChatMessage> chatHistory;
|
||||
|
||||
public OllamaChatResult(String response, long responseTime, int httpStatusCode,
|
||||
List<OllamaChatMessage> chatHistory) {
|
||||
super(response, responseTime, httpStatusCode);
|
||||
private final OllamaChatResponseModel responseModel;
|
||||
|
||||
public OllamaChatResult(OllamaChatResponseModel responseModel, List<OllamaChatMessage> chatHistory) {
|
||||
this.chatHistory = chatHistory;
|
||||
appendAnswerToChatHistory(response);
|
||||
this.responseModel = responseModel;
|
||||
appendAnswerToChatHistory(responseModel);
|
||||
}
|
||||
|
||||
public List<OllamaChatMessage> getChatHistory() {
|
||||
return chatHistory;
|
||||
}
|
||||
|
||||
private void appendAnswerToChatHistory(String answer){
|
||||
OllamaChatMessage assistantMessage = new OllamaChatMessage(OllamaChatMessageRole.ASSISTANT, answer);
|
||||
this.chatHistory.add(assistantMessage);
|
||||
private void appendAnswerToChatHistory(OllamaChatResponseModel response) {
|
||||
this.chatHistory.add(response.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this);
|
||||
} catch (JsonProcessingException e) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,31 +1,19 @@
|
||||
package io.github.ollama4j.models.chat;
|
||||
|
||||
import io.github.ollama4j.models.generate.OllamaStreamHandler;
|
||||
import io.github.ollama4j.models.generate.OllamaTokenHandler;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OllamaChatStreamObserver {
|
||||
|
||||
private OllamaStreamHandler streamHandler;
|
||||
|
||||
private List<OllamaChatResponseModel> responseParts = new ArrayList<>();
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class OllamaChatStreamObserver implements OllamaTokenHandler {
|
||||
private final OllamaStreamHandler streamHandler;
|
||||
private String message = "";
|
||||
|
||||
public OllamaChatStreamObserver(OllamaStreamHandler streamHandler) {
|
||||
this.streamHandler = streamHandler;
|
||||
@Override
|
||||
public void accept(OllamaChatResponseModel token) {
|
||||
if (streamHandler != null) {
|
||||
message += token.getMessage().getContent();
|
||||
streamHandler.accept(message);
|
||||
}
|
||||
}
|
||||
|
||||
public void notify(OllamaChatResponseModel currentResponsePart) {
|
||||
responseParts.add(currentResponsePart);
|
||||
handleCurrentResponsePart(currentResponsePart);
|
||||
}
|
||||
|
||||
protected void handleCurrentResponsePart(OllamaChatResponseModel currentResponsePart) {
|
||||
message = message + currentResponsePart.getMessage().getContent();
|
||||
streamHandler.accept(message);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.github.ollama4j.models.chat;
|
||||
|
||||
import io.github.ollama4j.tools.OllamaToolCallsFunction;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class OllamaChatToolCalls {
|
||||
|
||||
private OllamaToolCallsFunction function;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package io.github.ollama4j.models.embeddings;
|
||||
|
||||
import io.github.ollama4j.utils.Options;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Builder class to easily create Requests for Embedding models using ollama.
|
||||
*/
|
||||
public class OllamaEmbedRequestBuilder {
|
||||
|
||||
private final OllamaEmbedRequestModel request;
|
||||
|
||||
private OllamaEmbedRequestBuilder(String model, List<String> input) {
|
||||
this.request = new OllamaEmbedRequestModel(model,input);
|
||||
}
|
||||
|
||||
public static OllamaEmbedRequestBuilder getInstance(String model, String... input){
|
||||
return new OllamaEmbedRequestBuilder(model, List.of(input));
|
||||
}
|
||||
|
||||
public OllamaEmbedRequestBuilder withOptions(Options options){
|
||||
this.request.setOptions(options.getOptionsMap());
|
||||
return this;
|
||||
}
|
||||
|
||||
public OllamaEmbedRequestBuilder withKeepAlive(String keepAlive){
|
||||
this.request.setKeepAlive(keepAlive);
|
||||
return this;
|
||||
}
|
||||
|
||||
public OllamaEmbedRequestBuilder withoutTruncate(){
|
||||
this.request.setTruncate(false);
|
||||
return this;
|
||||
}
|
||||
|
||||
public OllamaEmbedRequestModel build() {
|
||||
return this.request;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
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;
|
||||
|
||||
import static io.github.ollama4j.utils.Utils.getObjectMapper;
|
||||
|
||||
@Data
|
||||
@RequiredArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class OllamaEmbedRequestModel {
|
||||
@NonNull
|
||||
private String model;
|
||||
|
||||
@NonNull
|
||||
private List<String> input;
|
||||
|
||||
private Map<String, Object> options;
|
||||
|
||||
@JsonProperty(value = "keep_alive")
|
||||
private String keepAlive;
|
||||
|
||||
@JsonProperty(value = "truncate")
|
||||
private Boolean truncate = true;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package io.github.ollama4j.models.embeddings;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Data
|
||||
public class OllamaEmbedResponseModel {
|
||||
@JsonProperty("model")
|
||||
private String model;
|
||||
|
||||
@JsonProperty("embeddings")
|
||||
private List<List<Double>> embeddings;
|
||||
|
||||
@JsonProperty("total_duration")
|
||||
private long totalDuration;
|
||||
|
||||
@JsonProperty("load_duration")
|
||||
private long loadDuration;
|
||||
|
||||
@JsonProperty("prompt_eval_count")
|
||||
private int promptEvalCount;
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import lombok.Data;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Data
|
||||
@Deprecated(since="1.0.90")
|
||||
public class OllamaEmbeddingResponseModel {
|
||||
@JsonProperty("embedding")
|
||||
private List<Double> embedding;
|
||||
|
||||
@@ -2,6 +2,7 @@ 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){
|
||||
|
||||
@@ -12,6 +12,7 @@ import lombok.RequiredArgsConstructor;
|
||||
@Data
|
||||
@RequiredArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Deprecated(since="1.0.90")
|
||||
public class OllamaEmbeddingsRequestModel {
|
||||
@NonNull
|
||||
private String model;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package io.github.ollama4j.models.generate;
|
||||
|
||||
import io.github.ollama4j.models.chat.OllamaChatResponseModel;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface OllamaTokenHandler extends Consumer<OllamaChatResponseModel> {
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
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 lombok.Data;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class CustomModelRequest {
|
||||
private String model;
|
||||
private String from;
|
||||
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 String system;
|
||||
private Map<String, Object> parameters;
|
||||
private List<Object> messages;
|
||||
private Boolean stream;
|
||||
private Boolean quantize;
|
||||
|
||||
public CustomModelRequest() {
|
||||
this.stream = true;
|
||||
this.quantize = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,25 @@
|
||||
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.models.response.OllamaResult;
|
||||
import io.github.ollama4j.models.chat.OllamaChatResponseModel;
|
||||
import io.github.ollama4j.models.chat.OllamaChatStreamObserver;
|
||||
import io.github.ollama4j.models.generate.OllamaStreamHandler;
|
||||
import io.github.ollama4j.utils.OllamaRequestBody;
|
||||
import io.github.ollama4j.models.chat.*;
|
||||
import io.github.ollama4j.models.generate.OllamaTokenHandler;
|
||||
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;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Specialization class for requests
|
||||
@@ -20,7 +28,7 @@ public class OllamaChatEndpointCaller extends OllamaEndpointCaller {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(OllamaChatEndpointCaller.class);
|
||||
|
||||
private OllamaChatStreamObserver streamObserver;
|
||||
private OllamaTokenHandler tokenHandler;
|
||||
|
||||
public OllamaChatEndpointCaller(String host, BasicAuth basicAuth, long requestTimeoutSeconds, boolean verbose) {
|
||||
super(host, basicAuth, requestTimeoutSeconds, verbose);
|
||||
@@ -31,13 +39,29 @@ public class OllamaChatEndpointCaller extends OllamaEndpointCaller {
|
||||
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.
|
||||
*
|
||||
* @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) {
|
||||
try {
|
||||
OllamaChatResponseModel ollamaResponseModel = Utils.getObjectMapper().readValue(line, OllamaChatResponseModel.class);
|
||||
responseBuffer.append(ollamaResponseModel.getMessage().getContent());
|
||||
if (streamObserver != null) {
|
||||
streamObserver.notify(ollamaResponseModel);
|
||||
// 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) {
|
||||
responseBuffer.append(message.getContent());
|
||||
if (tokenHandler != null) {
|
||||
tokenHandler.accept(ollamaResponseModel);
|
||||
}
|
||||
}
|
||||
return ollamaResponseModel.isDone();
|
||||
} catch (JsonProcessingException e) {
|
||||
@@ -46,9 +70,75 @@ public class OllamaChatEndpointCaller extends OllamaEndpointCaller {
|
||||
}
|
||||
}
|
||||
|
||||
public OllamaResult call(OllamaRequestBody body, OllamaStreamHandler streamHandler)
|
||||
public OllamaChatResult call(OllamaChatRequest body, OllamaTokenHandler tokenHandler)
|
||||
throws OllamaBaseException, IOException, InterruptedException {
|
||||
streamObserver = new OllamaChatStreamObserver(streamHandler);
|
||||
return super.callSync(body);
|
||||
this.tokenHandler = tokenHandler;
|
||||
return callSync(body);
|
||||
}
|
||||
|
||||
public OllamaChatResult callSync(OllamaChatRequest body) throws OllamaBaseException, IOException, InterruptedException {
|
||||
// Create Request
|
||||
HttpClient httpClient = HttpClient.newHttpClient();
|
||||
URI uri = URI.create(getHost() + getEndpointSuffix());
|
||||
HttpRequest.Builder requestBuilder =
|
||||
getRequestBuilderDefault(uri)
|
||||
.POST(
|
||||
body.getBodyPublisher());
|
||||
HttpRequest request = requestBuilder.build();
|
||||
if (isVerbose()) LOG.info("Asking model: " + body);
|
||||
HttpResponse<InputStream> response =
|
||||
httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
|
||||
|
||||
int statusCode = response.statusCode();
|
||||
InputStream responseBodyStream = response.body();
|
||||
StringBuilder responseBuffer = new StringBuilder();
|
||||
OllamaChatResponseModel ollamaChatResponseModel = null;
|
||||
List<OllamaChatToolCalls> wantedToolsForStream = null;
|
||||
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 {
|
||||
boolean finished = parseResponseAndAddToBuffer(line, responseBuffer);
|
||||
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());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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());
|
||||
if (isVerbose()) LOG.info("Model response: " + ollamaResult);
|
||||
return ollamaResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.github.ollama4j.models.response.OllamaErrorResponse;
|
||||
import io.github.ollama4j.models.response.OllamaResult;
|
||||
import io.github.ollama4j.utils.OllamaRequestBody;
|
||||
import io.github.ollama4j.utils.Utils;
|
||||
import lombok.Getter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -24,14 +25,15 @@ import java.util.Base64;
|
||||
/**
|
||||
* Abstract helperclass to call the ollama api server.
|
||||
*/
|
||||
@Getter
|
||||
public abstract class OllamaEndpointCaller {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(OllamaAPI.class);
|
||||
|
||||
private String host;
|
||||
private BasicAuth basicAuth;
|
||||
private long requestTimeoutSeconds;
|
||||
private boolean verbose;
|
||||
private final String host;
|
||||
private final BasicAuth basicAuth;
|
||||
private final long requestTimeoutSeconds;
|
||||
private final boolean verbose;
|
||||
|
||||
public OllamaEndpointCaller(String host, BasicAuth basicAuth, long requestTimeoutSeconds, boolean verbose) {
|
||||
this.host = host;
|
||||
@@ -45,80 +47,13 @@ public abstract class OllamaEndpointCaller {
|
||||
protected abstract boolean parseResponseAndAddToBuffer(String line, StringBuilder responseBuffer);
|
||||
|
||||
|
||||
/**
|
||||
* 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 InterruptedException in case the server is not reachable or network issues happen
|
||||
*/
|
||||
public OllamaResult callSync(OllamaRequestBody body) throws OllamaBaseException, IOException, InterruptedException {
|
||||
// Create Request
|
||||
long startTime = System.currentTimeMillis();
|
||||
HttpClient httpClient = HttpClient.newHttpClient();
|
||||
URI uri = URI.create(this.host + getEndpointSuffix());
|
||||
HttpRequest.Builder requestBuilder =
|
||||
getRequestBuilderDefault(uri)
|
||||
.POST(
|
||||
body.getBodyPublisher());
|
||||
HttpRequest request = requestBuilder.build();
|
||||
if (this.verbose) LOG.info("Asking model: " + body.toString());
|
||||
HttpResponse<InputStream> response =
|
||||
httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
|
||||
|
||||
int statusCode = response.statusCode();
|
||||
InputStream responseBodyStream = response.body();
|
||||
StringBuilder responseBuffer = new StringBuilder();
|
||||
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 {
|
||||
boolean finished = parseResponseAndAddToBuffer(line, responseBuffer);
|
||||
if (finished) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (statusCode != 200) {
|
||||
LOG.error("Status code " + statusCode);
|
||||
throw new OllamaBaseException(responseBuffer.toString());
|
||||
} else {
|
||||
long endTime = System.currentTimeMillis();
|
||||
OllamaResult ollamaResult =
|
||||
new OllamaResult(responseBuffer.toString().trim(), endTime - startTime, statusCode);
|
||||
if (verbose) LOG.info("Model response: " + ollamaResult);
|
||||
return ollamaResult;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default request builder.
|
||||
*
|
||||
* @param uri URI to get a HttpRequest.Builder
|
||||
* @return HttpRequest.Builder
|
||||
*/
|
||||
private HttpRequest.Builder getRequestBuilderDefault(URI uri) {
|
||||
protected HttpRequest.Builder getRequestBuilderDefault(URI uri) {
|
||||
HttpRequest.Builder requestBuilder =
|
||||
HttpRequest.newBuilder(uri)
|
||||
.header("Content-Type", "application/json")
|
||||
@@ -134,7 +69,7 @@ public abstract class OllamaEndpointCaller {
|
||||
*
|
||||
* @return basic authentication header value (encoded credentials)
|
||||
*/
|
||||
private String getBasicAuthHeaderValue() {
|
||||
protected String getBasicAuthHeaderValue() {
|
||||
String credentialsToEncode = this.basicAuth.getUsername() + ":" + this.basicAuth.getPassword();
|
||||
return "Basic " + Base64.getEncoder().encodeToString(credentialsToEncode.getBytes());
|
||||
}
|
||||
@@ -144,7 +79,7 @@ public abstract class OllamaEndpointCaller {
|
||||
*
|
||||
* @return true when Basic Auth credentials set
|
||||
*/
|
||||
private boolean isBasicAuthCredentialsSet() {
|
||||
protected boolean isBasicAuthCredentialsSet() {
|
||||
return this.basicAuth != null;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.github.ollama4j.models.request;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import io.github.ollama4j.exceptions.OllamaBaseException;
|
||||
import io.github.ollama4j.models.response.OllamaErrorResponse;
|
||||
import io.github.ollama4j.models.response.OllamaResult;
|
||||
import io.github.ollama4j.models.generate.OllamaGenerateResponseModel;
|
||||
import io.github.ollama4j.models.generate.OllamaGenerateStreamObserver;
|
||||
@@ -11,7 +12,15 @@ 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;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class OllamaGenerateEndpointCaller extends OllamaEndpointCaller {
|
||||
|
||||
@@ -46,6 +55,73 @@ public class OllamaGenerateEndpointCaller extends OllamaEndpointCaller {
|
||||
public OllamaResult call(OllamaRequestBody body, OllamaStreamHandler streamHandler)
|
||||
throws OllamaBaseException, IOException, InterruptedException {
|
||||
streamObserver = new OllamaGenerateStreamObserver(streamHandler);
|
||||
return super.callSync(body);
|
||||
return callSync(body);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 InterruptedException in case the server is not reachable or network issues happen
|
||||
*/
|
||||
public OllamaResult callSync(OllamaRequestBody body) throws OllamaBaseException, IOException, InterruptedException {
|
||||
// Create Request
|
||||
long startTime = System.currentTimeMillis();
|
||||
HttpClient httpClient = HttpClient.newHttpClient();
|
||||
URI uri = URI.create(getHost() + getEndpointSuffix());
|
||||
HttpRequest.Builder requestBuilder =
|
||||
getRequestBuilderDefault(uri)
|
||||
.POST(
|
||||
body.getBodyPublisher());
|
||||
HttpRequest request = requestBuilder.build();
|
||||
if (isVerbose()) LOG.info("Asking model: " + body.toString());
|
||||
HttpResponse<InputStream> response =
|
||||
httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
|
||||
|
||||
int statusCode = response.statusCode();
|
||||
InputStream responseBodyStream = response.body();
|
||||
StringBuilder responseBuffer = new StringBuilder();
|
||||
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 {
|
||||
boolean finished = parseResponseAndAddToBuffer(line, responseBuffer);
|
||||
if (finished) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (statusCode != 200) {
|
||||
LOG.error("Status code " + statusCode);
|
||||
throw new OllamaBaseException(responseBuffer.toString());
|
||||
} else {
|
||||
long endTime = System.currentTimeMillis();
|
||||
OllamaResult ollamaResult =
|
||||
new OllamaResult(responseBuffer.toString().trim(), endTime - startTime, statusCode);
|
||||
if (isVerbose()) LOG.info("Model response: " + ollamaResult);
|
||||
return ollamaResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.github.ollama4j.models.response;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@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;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.github.ollama4j.models.response;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class LibraryModelDetail {
|
||||
|
||||
private LibraryModel model;
|
||||
private List<LibraryModelTag> tags;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package io.github.ollama4j.models.response;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class LibraryModelTag {
|
||||
private String name;
|
||||
private String tag;
|
||||
private String size;
|
||||
private String lastUpdated;
|
||||
}
|
||||
@@ -2,12 +2,14 @@ package io.github.ollama4j.models.response;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
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;
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Model {
|
||||
|
||||
private String name;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package io.github.ollama4j.models.response;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class OllamaVersion {
|
||||
private String version;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package io.github.ollama4j.tools;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class OllamaToolCallsFunction
|
||||
{
|
||||
private String name;
|
||||
private Map<String,Object> arguments;
|
||||
}
|
||||
@@ -18,6 +18,9 @@ public class OllamaToolsResult {
|
||||
|
||||
public List<ToolResult> getToolResults() {
|
||||
List<ToolResult> results = new ArrayList<>();
|
||||
if (this.toolResults == null) {
|
||||
return results;
|
||||
}
|
||||
for (Map.Entry<ToolFunctionCallSpec, Object> r : this.toolResults.entrySet()) {
|
||||
results.add(new ToolResult(r.getKey().getName(), r.getKey().getArguments(), r.getValue()));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* Specification of a {@link ToolFunction} that provides the implementation via java reflection calling.
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class ReflectionalToolFunction implements ToolFunction{
|
||||
|
||||
private Object functionHolder;
|
||||
private Method function;
|
||||
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()));
|
||||
}
|
||||
try {
|
||||
return function.invoke(functionHolder, argumentsCopy.values().toArray());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to invoke tool: " + function.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private Object typeCast(Object inputValue, String className) {
|
||||
if(className == null || inputValue == null) {
|
||||
return null;
|
||||
}
|
||||
String inputValueString = inputValue.toString();
|
||||
switch (className) {
|
||||
case "java.lang.Integer":
|
||||
return Integer.parseInt(inputValueString);
|
||||
case "java.lang.Boolean":
|
||||
return Boolean.valueOf(inputValueString);
|
||||
case "java.math.BigDecimal":
|
||||
return new BigDecimal(inputValueString);
|
||||
default:
|
||||
return inputValueString;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +1,22 @@
|
||||
package io.github.ollama4j.tools;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ToolRegistry {
|
||||
private final Map<String, ToolFunction> functionMap = new HashMap<>();
|
||||
private final Map<String, Tools.ToolSpecification> tools = new HashMap<>();
|
||||
|
||||
public ToolFunction getFunction(String name) {
|
||||
return functionMap.get(name);
|
||||
public ToolFunction getToolFunction(String name) {
|
||||
final Tools.ToolSpecification toolSpecification = tools.get(name);
|
||||
return toolSpecification !=null ? toolSpecification.getToolFunction() : null ;
|
||||
}
|
||||
|
||||
public void addFunction(String name, ToolFunction function) {
|
||||
functionMap.put(name, function);
|
||||
public void addTool (String name, Tools.ToolSpecification specification) {
|
||||
tools.put(name, specification);
|
||||
}
|
||||
|
||||
public Collection<Tools.ToolSpecification> getRegisteredSpecs(){
|
||||
return tools.values();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@ 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 lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -20,17 +22,23 @@ public class Tools {
|
||||
public static class ToolSpecification {
|
||||
private String functionName;
|
||||
private String functionDescription;
|
||||
private Map<String, PromptFuncDefinition.Property> properties;
|
||||
private ToolFunction toolDefinition;
|
||||
private PromptFuncDefinition toolPrompt;
|
||||
private ToolFunction toolFunction;
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class PromptFuncDefinition {
|
||||
private String type;
|
||||
private PromptFuncSpec function;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class PromptFuncSpec {
|
||||
private String name;
|
||||
private String description;
|
||||
@@ -38,6 +46,9 @@ public class Tools {
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class Parameters {
|
||||
private String type;
|
||||
private Map<String, Property> properties;
|
||||
@@ -46,6 +57,8 @@ public class Tools {
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class Property {
|
||||
private String type;
|
||||
private String description;
|
||||
@@ -94,10 +107,10 @@ public class Tools {
|
||||
|
||||
PromptFuncDefinition.Parameters parameters = new PromptFuncDefinition.Parameters();
|
||||
parameters.setType("object");
|
||||
parameters.setProperties(spec.getProperties());
|
||||
parameters.setProperties(spec.getToolPrompt().getFunction().parameters.getProperties());
|
||||
|
||||
List<String> requiredValues = new ArrayList<>();
|
||||
for (Map.Entry<String, PromptFuncDefinition.Property> p : spec.getProperties().entrySet()) {
|
||||
for (Map.Entry<String, PromptFuncDefinition.Property> p : spec.getToolPrompt().getFunction().getParameters().getProperties().entrySet()) {
|
||||
if (p.getValue().isRequired()) {
|
||||
requiredValues.add(p.getKey());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
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}).
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface OllamaToolService {
|
||||
|
||||
/**
|
||||
* @return Classes with no-arg constructor that will be used for tool-registration.
|
||||
*/
|
||||
Class<?>[] providers();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.github.ollama4j.tools.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotates a Method Parameter in a {@link ToolSpec} annotated Method. A parameter annotated with this annotation will
|
||||
* be part of the tool description that is sent to the llm for tool-calling.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface ToolProperty {
|
||||
|
||||
/**
|
||||
* @return name of the parameter that is used for the tool description. Has to be set as depending on the caller,
|
||||
* method name backtracking is not possible with reflection.
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* @return a detailed description of the parameter. This is used by the llm called to specify, which property has
|
||||
* to be set by the llm and how this should be filled.
|
||||
*/
|
||||
String desc();
|
||||
|
||||
/**
|
||||
* @return tells the llm that it has to set a value for this property.
|
||||
*/
|
||||
boolean required() default true;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
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.
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ToolSpec {
|
||||
|
||||
/**
|
||||
* @return tool-name that the method should be used as. Defaults to the methods 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.
|
||||
*/
|
||||
String desc();
|
||||
}
|
||||
@@ -10,14 +10,12 @@ package io.github.ollama4j.types;
|
||||
public class OllamaModelType {
|
||||
public static final String GEMMA = "gemma";
|
||||
public static final String GEMMA2 = "gemma2";
|
||||
|
||||
|
||||
public static final String LLAMA2 = "llama2";
|
||||
public static final String LLAMA3 = "llama3";
|
||||
public static final String LLAMA3_1 = "llama3.1";
|
||||
|
||||
public static final String MISTRAL = "mistral";
|
||||
public static final String MIXTRAL = "mixtral";
|
||||
public static final String DEEPSEEK_R1 = "deepseek-r1";
|
||||
public static final String LLAVA = "llava";
|
||||
public static final String LLAVA_PHI3 = "llava-phi3";
|
||||
public static final String NEURAL_CHAT = "neural-chat";
|
||||
@@ -35,7 +33,6 @@ public class OllamaModelType {
|
||||
public static final String ZEPHYR = "zephyr";
|
||||
public static final String OPENHERMES = "openhermes";
|
||||
public static final String QWEN = "qwen";
|
||||
|
||||
public static final String QWEN2 = "qwen2";
|
||||
public static final String WIZARDCODER = "wizardcoder";
|
||||
public static final String LLAMA2_CHINESE = "llama2-chinese";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.github.ollama4j.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
/** Builder class for creating options for Ollama model. */
|
||||
@@ -207,6 +208,34 @@ public class OptionsBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alternative to the top_p, and aims to ensure a balance of qualityand variety. The parameter p
|
||||
* represents the minimum probability for a token to be considered, relative to the probability
|
||||
* of the most likely token. For example, with p=0.05 and the most likely token having a
|
||||
* probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)
|
||||
*/
|
||||
public OptionsBuilder setMinP(float value) {
|
||||
options.getOptionsMap().put("min_p", value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows passing an option not formally supported by the library
|
||||
* @param name The option name for the parameter.
|
||||
* @param value The value for the "{name}" parameter.
|
||||
* @return The updated OptionsBuilder.
|
||||
* @throws IllegalArgumentException if parameter has an unsupported type
|
||||
*/
|
||||
public OptionsBuilder setCustomOption(String name, Object value) throws IllegalArgumentException {
|
||||
if (!(value instanceof Integer || value instanceof Float || value instanceof String)) {
|
||||
throw new IllegalArgumentException("Invalid type for parameter. Allowed types are: Integer, Float, or String.");
|
||||
}
|
||||
options.getOptionsMap().put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Builds the options map.
|
||||
*
|
||||
@@ -215,4 +244,6 @@ public class OptionsBuilder {
|
||||
public Options build() {
|
||||
return options;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
package io.github.ollama4j.integrationtests;
|
||||
|
||||
import io.github.ollama4j.OllamaAPI;
|
||||
import io.github.ollama4j.exceptions.OllamaBaseException;
|
||||
import io.github.ollama4j.models.chat.OllamaChatMessageRole;
|
||||
import io.github.ollama4j.models.chat.OllamaChatRequest;
|
||||
import io.github.ollama4j.models.chat.OllamaChatRequestBuilder;
|
||||
import io.github.ollama4j.models.chat.OllamaChatResult;
|
||||
import io.github.ollama4j.models.embeddings.OllamaEmbedResponseModel;
|
||||
import io.github.ollama4j.models.response.LibraryModel;
|
||||
import io.github.ollama4j.models.response.Model;
|
||||
import io.github.ollama4j.models.response.ModelDetail;
|
||||
import io.github.ollama4j.models.response.OllamaResult;
|
||||
import io.github.ollama4j.utils.OptionsBuilder;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.ollama.OllamaContainer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@SuppressWarnings("HttpUrlsUsage")
|
||||
public class OllamaAPIIntegrationTest {
|
||||
|
||||
private static OllamaContainer ollama;
|
||||
private static OllamaAPI api;
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
String version = "0.5.13";
|
||||
int internalPort = 11434;
|
||||
int mappedPort = 11435;
|
||||
ollama = new OllamaContainer("ollama/ollama:" + version);
|
||||
ollama.addExposedPort(internalPort);
|
||||
List<String> portBindings = new ArrayList<>();
|
||||
portBindings.add(mappedPort + ":" + internalPort);
|
||||
ollama.setPortBindings(portBindings);
|
||||
ollama.start();
|
||||
api = new OllamaAPI("http://" + ollama.getHost() + ":" + ollama.getMappedPort(internalPort));
|
||||
api.setRequestTimeoutSeconds(60);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
void testWrongEndpoint() {
|
||||
OllamaAPI ollamaAPI = new OllamaAPI("http://wrong-host:11434");
|
||||
assertThrows(ConnectException.class, ollamaAPI::listModels);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testVersionAPI() throws URISyntaxException, IOException, OllamaBaseException, InterruptedException {
|
||||
String expectedVersion = ollama.getDockerImageName().split(":")[1];
|
||||
String actualVersion = api.getVersion();
|
||||
assertEquals(expectedVersion, actualVersion, "Version should match the Docker image version");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void testListModelsAPI() throws URISyntaxException, IOException, OllamaBaseException, InterruptedException {
|
||||
// Fetch the list of models
|
||||
List<Model> models = api.listModels();
|
||||
// Assert that the models list is not null
|
||||
assertNotNull(models, "Models should not be null");
|
||||
// Assert that models list is either empty or contains more than 0 models
|
||||
assertTrue(models.size() >= 0, "Models list should be empty or contain elements");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
void testListModelsFromLibrary() throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
|
||||
List<LibraryModel> models = api.listModelsFromLibrary();
|
||||
assertNotNull(models);
|
||||
assertFalse(models.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void testPullModelAPI() throws URISyntaxException, IOException, OllamaBaseException, InterruptedException {
|
||||
api.pullModel("all-minilm");
|
||||
List<Model> models = api.listModels();
|
||||
assertNotNull(models, "Models should not be null");
|
||||
assertFalse(models.isEmpty(), "Models list should contain elements");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
void testListModelDetails() throws IOException, OllamaBaseException, URISyntaxException, InterruptedException {
|
||||
String embeddingModelMinilm = "all-minilm";
|
||||
api.pullModel(embeddingModelMinilm);
|
||||
ModelDetail modelDetails = api.getModelDetails("all-minilm");
|
||||
assertNotNull(modelDetails);
|
||||
assertTrue(modelDetails.getModelFile().contains(embeddingModelMinilm));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
public void testGenerateEmbeddings() throws Exception {
|
||||
String embeddingModelMinilm = "all-minilm";
|
||||
api.pullModel(embeddingModelMinilm);
|
||||
OllamaEmbedResponseModel embeddings = api.embed(embeddingModelMinilm, Arrays.asList("Why is the sky blue?", "Why is the grass green?"));
|
||||
assertNotNull(embeddings, "Embeddings should not be null");
|
||||
assertFalse(embeddings.getEmbeddings().isEmpty(), "Embeddings should not be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(6)
|
||||
void testAskModelWithDefaultOptions() throws OllamaBaseException, IOException, InterruptedException, URISyntaxException {
|
||||
String chatModel = "qwen2.5:0.5b";
|
||||
api.pullModel(chatModel);
|
||||
OllamaResult result =
|
||||
api.generate(
|
||||
chatModel,
|
||||
"What is the capital of France? And what's France's connection with Mona Lisa?",
|
||||
false,
|
||||
new OptionsBuilder().build());
|
||||
assertNotNull(result);
|
||||
assertNotNull(result.getResponse());
|
||||
assertFalse(result.getResponse().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(7)
|
||||
void testAskModelWithDefaultOptionsStreamed() throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
|
||||
String chatModel = "qwen2.5:0.5b";
|
||||
api.pullModel(chatModel);
|
||||
StringBuffer sb = new StringBuffer();
|
||||
OllamaResult result = api.generate(chatModel,
|
||||
"What is the capital of France? And what's France's connection with Mona Lisa?",
|
||||
false,
|
||||
new OptionsBuilder().build(), (s) -> {
|
||||
System.out.println(s);
|
||||
String substring = s.substring(sb.toString().length(), s.length());
|
||||
System.out.println(substring);
|
||||
sb.append(substring);
|
||||
});
|
||||
|
||||
assertNotNull(result);
|
||||
assertNotNull(result.getResponse());
|
||||
assertFalse(result.getResponse().isEmpty());
|
||||
assertEquals(sb.toString().trim(), result.getResponse().trim());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(8)
|
||||
void testAskModelWithOptions() throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
|
||||
String chatModel = "qwen2.5:0.5b-instruct";
|
||||
api.pullModel(chatModel);
|
||||
|
||||
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(chatModel);
|
||||
OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.SYSTEM, "You are a helpful assistant who can generate random person's first and last names in the format [First name, Last name].")
|
||||
.build();
|
||||
requestModel = builder.withMessages(requestModel.getMessages())
|
||||
.withMessage(OllamaChatMessageRole.USER, "Give me a cool name")
|
||||
.withOptions(new OptionsBuilder().setTemperature(0.5f).build()).build();
|
||||
OllamaChatResult chatResult = api.chat(requestModel);
|
||||
|
||||
assertNotNull(chatResult);
|
||||
assertNotNull(chatResult.getResponseModel());
|
||||
assertFalse(chatResult.getResponseModel().getMessage().getContent().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(9)
|
||||
void testChatWithSystemPrompt() throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
|
||||
String chatModel = "llama3.2:1b";
|
||||
api.pullModel(chatModel);
|
||||
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(chatModel);
|
||||
OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.SYSTEM,
|
||||
"You are a silent bot that only says 'Shush'. Do not say anything else under any circumstances!")
|
||||
.withMessage(OllamaChatMessageRole.USER,
|
||||
"What's something that's brown and sticky?")
|
||||
.withOptions(new OptionsBuilder().setTemperature(0.8f).build())
|
||||
.build();
|
||||
|
||||
OllamaChatResult chatResult = api.chat(requestModel);
|
||||
assertNotNull(chatResult);
|
||||
assertNotNull(chatResult.getResponseModel());
|
||||
assertNotNull(chatResult.getResponseModel().getMessage());
|
||||
assertFalse(chatResult.getResponseModel().getMessage().getContent().isBlank());
|
||||
assertTrue(chatResult.getResponseModel().getMessage().getContent().contains("Shush"));
|
||||
assertEquals(3, chatResult.getChatHistory().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(10)
|
||||
public void testChat() throws Exception {
|
||||
String chatModel = "qwen2.5:0.5b";
|
||||
api.pullModel(chatModel);
|
||||
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(chatModel);
|
||||
|
||||
// Create the initial user question
|
||||
OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.USER, "What is the capital of France?")
|
||||
.build();
|
||||
|
||||
// Start conversation with model
|
||||
OllamaChatResult chatResult = api.chat(requestModel);
|
||||
|
||||
assertTrue(
|
||||
chatResult.getChatHistory().stream()
|
||||
.anyMatch(chat -> chat.getContent().contains("Paris")),
|
||||
"Expected chat history to contain 'Paris'"
|
||||
);
|
||||
|
||||
// Create the next user question: second largest city
|
||||
requestModel = builder.withMessages(chatResult.getChatHistory())
|
||||
.withMessage(OllamaChatMessageRole.USER, "And what is its official language?")
|
||||
.build();
|
||||
|
||||
// Continue conversation with model
|
||||
chatResult = api.chat(requestModel);
|
||||
|
||||
assertTrue(
|
||||
chatResult.getChatHistory().stream()
|
||||
.anyMatch(chat -> chat.getContent().contains("French")),
|
||||
"Expected chat history to contain 'French'"
|
||||
);
|
||||
|
||||
// Create the next user question: the third question
|
||||
requestModel = builder.withMessages(chatResult.getChatHistory())
|
||||
.withMessage(OllamaChatMessageRole.USER, "What is the largest river in France?")
|
||||
.build();
|
||||
|
||||
// Continue conversation with the model for the third question
|
||||
chatResult = api.chat(requestModel);
|
||||
|
||||
// verify the result
|
||||
assertNotNull(chatResult, "Chat result should not be null");
|
||||
assertTrue(chatResult.getChatHistory().size() > 2, "Chat history should contain more than two messages");
|
||||
assertTrue(chatResult.getChatHistory().get(chatResult.getChatHistory().size() - 1).getContent().contains("river"), "Response should be related to river");
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
21
src/test/java/io/github/ollama4j/samples/AnnotatedTool.java
Normal file
21
src/test/java/io/github/ollama4j/samples/AnnotatedTool.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package io.github.ollama4j.samples;
|
||||
|
||||
import io.github.ollama4j.tools.annotations.ToolProperty;
|
||||
import io.github.ollama4j.tools.annotations.ToolSpec;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class AnnotatedTool {
|
||||
|
||||
@ToolSpec(desc = "Computes the most important constant all around the globe!")
|
||||
public String computeImportantConstant(@ToolProperty(name = "noOfDigits",desc = "Number of digits that shall be returned") Integer noOfDigits ){
|
||||
return BigDecimal.valueOf((long)(Math.random()*1000000L),noOfDigits).toString();
|
||||
}
|
||||
|
||||
@ToolSpec(desc = "Says hello to a friend!")
|
||||
public String sayHello(@ToolProperty(name = "name",desc = "Name of the friend") String name, Integer someRandomProperty, @ToolProperty(name="amountOfHearts",desc = "amount of heart emojis that should be used", required = false) Integer amountOfHearts) {
|
||||
String hearts = amountOfHearts!=null ? "♡".repeat(amountOfHearts) : "";
|
||||
return "Hello " + name +" ("+someRandomProperty+") " + hearts;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,11 @@ package io.github.ollama4j.unittests;
|
||||
|
||||
import io.github.ollama4j.OllamaAPI;
|
||||
import io.github.ollama4j.exceptions.OllamaBaseException;
|
||||
import io.github.ollama4j.exceptions.RoleNotFoundException;
|
||||
import io.github.ollama4j.models.chat.OllamaChatMessageRole;
|
||||
import io.github.ollama4j.models.embeddings.OllamaEmbedRequestModel;
|
||||
import io.github.ollama4j.models.embeddings.OllamaEmbedResponseModel;
|
||||
import io.github.ollama4j.models.request.CustomModelRequest;
|
||||
import io.github.ollama4j.models.response.ModelDetail;
|
||||
import io.github.ollama4j.models.response.OllamaAsyncResultStreamer;
|
||||
import io.github.ollama4j.models.response.OllamaResult;
|
||||
@@ -14,7 +19,9 @@ import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class TestMockedAPIs {
|
||||
@@ -46,12 +53,11 @@ class TestMockedAPIs {
|
||||
@Test
|
||||
void testCreateModel() {
|
||||
OllamaAPI ollamaAPI = Mockito.mock(OllamaAPI.class);
|
||||
String model = OllamaModelType.LLAMA2;
|
||||
String modelFilePath = "FROM llama2\nSYSTEM You are mario from Super Mario Bros.";
|
||||
CustomModelRequest customModelRequest = CustomModelRequest.builder().model("mario").from("llama3.2:latest").system("You are Mario from Super Mario Bros.").build();
|
||||
try {
|
||||
doNothing().when(ollamaAPI).createModelWithModelFileContents(model, modelFilePath);
|
||||
ollamaAPI.createModelWithModelFileContents(model, modelFilePath);
|
||||
verify(ollamaAPI, times(1)).createModelWithModelFileContents(model, modelFilePath);
|
||||
doNothing().when(ollamaAPI).createModel(customModelRequest);
|
||||
ollamaAPI.createModel(customModelRequest);
|
||||
verify(ollamaAPI, times(1)).createModel(customModelRequest);
|
||||
} catch (IOException | OllamaBaseException | InterruptedException | URISyntaxException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -97,6 +103,34 @@ class TestMockedAPIs {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmbed() {
|
||||
OllamaAPI ollamaAPI = Mockito.mock(OllamaAPI.class);
|
||||
String model = OllamaModelType.LLAMA2;
|
||||
List<String> inputs = List.of("some prompt text");
|
||||
try {
|
||||
when(ollamaAPI.embed(model, inputs)).thenReturn(new OllamaEmbedResponseModel());
|
||||
ollamaAPI.embed(model, inputs);
|
||||
verify(ollamaAPI, times(1)).embed(model, inputs);
|
||||
} catch (IOException | OllamaBaseException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmbedWithEmbedRequestModel() {
|
||||
OllamaAPI ollamaAPI = Mockito.mock(OllamaAPI.class);
|
||||
String model = OllamaModelType.LLAMA2;
|
||||
List<String> inputs = List.of("some prompt text");
|
||||
try {
|
||||
when(ollamaAPI.embed(new OllamaEmbedRequestModel(model, inputs))).thenReturn(new OllamaEmbedResponseModel());
|
||||
ollamaAPI.embed(new OllamaEmbedRequestModel(model, inputs));
|
||||
verify(ollamaAPI, times(1)).embed(new OllamaEmbedRequestModel(model, inputs));
|
||||
} catch (IOException | OllamaBaseException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAsk() {
|
||||
OllamaAPI ollamaAPI = Mockito.mock(OllamaAPI.class);
|
||||
@@ -161,4 +195,68 @@ class TestMockedAPIs {
|
||||
ollamaAPI.generateAsync(model, prompt, false);
|
||||
verify(ollamaAPI, times(1)).generateAsync(model, prompt, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddCustomRole() {
|
||||
OllamaAPI ollamaAPI = mock(OllamaAPI.class);
|
||||
String roleName = "custom-role";
|
||||
OllamaChatMessageRole expectedRole = OllamaChatMessageRole.newCustomRole(roleName);
|
||||
when(ollamaAPI.addCustomRole(roleName)).thenReturn(expectedRole);
|
||||
OllamaChatMessageRole customRole = ollamaAPI.addCustomRole(roleName);
|
||||
assertEquals(expectedRole, customRole);
|
||||
verify(ollamaAPI, times(1)).addCustomRole(roleName);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testListRoles() {
|
||||
OllamaAPI ollamaAPI = Mockito.mock(OllamaAPI.class);
|
||||
OllamaChatMessageRole role1 = OllamaChatMessageRole.newCustomRole("role1");
|
||||
OllamaChatMessageRole role2 = OllamaChatMessageRole.newCustomRole("role2");
|
||||
List<OllamaChatMessageRole> expectedRoles = List.of(role1, role2);
|
||||
when(ollamaAPI.listRoles()).thenReturn(expectedRoles);
|
||||
List<OllamaChatMessageRole> actualRoles = ollamaAPI.listRoles();
|
||||
assertEquals(expectedRoles, actualRoles);
|
||||
verify(ollamaAPI, times(1)).listRoles();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetRoleNotFound() {
|
||||
OllamaAPI ollamaAPI = mock(OllamaAPI.class);
|
||||
String roleName = "non-existing-role";
|
||||
try {
|
||||
when(ollamaAPI.getRole(roleName)).thenThrow(new RoleNotFoundException("Role not found"));
|
||||
} catch (RoleNotFoundException exception) {
|
||||
throw new RuntimeException("Failed to run test: testGetRoleNotFound");
|
||||
}
|
||||
try {
|
||||
ollamaAPI.getRole(roleName);
|
||||
fail("Expected RoleNotFoundException not thrown");
|
||||
} catch (RoleNotFoundException exception) {
|
||||
assertEquals("Role not found", exception.getMessage());
|
||||
}
|
||||
try {
|
||||
verify(ollamaAPI, times(1)).getRole(roleName);
|
||||
} catch (RoleNotFoundException exception) {
|
||||
throw new RuntimeException("Failed to run test: testGetRoleNotFound");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetRoleFound() {
|
||||
OllamaAPI ollamaAPI = mock(OllamaAPI.class);
|
||||
String roleName = "existing-role";
|
||||
OllamaChatMessageRole expectedRole = OllamaChatMessageRole.newCustomRole(roleName);
|
||||
try {
|
||||
when(ollamaAPI.getRole(roleName)).thenReturn(expectedRole);
|
||||
} catch (RoleNotFoundException exception) {
|
||||
throw new RuntimeException("Failed to run test: testGetRoleFound");
|
||||
}
|
||||
try {
|
||||
OllamaChatMessageRole actualRole = ollamaAPI.getRole(roleName);
|
||||
assertEquals(expectedRole, actualRole);
|
||||
verify(ollamaAPI, times(1)).getRole(roleName);
|
||||
} catch (RoleNotFoundException exception) {
|
||||
throw new RuntimeException("Failed to run test: testGetRoleFound");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package io.github.ollama4j.unittests.jackson;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.github.ollama4j.models.chat.OllamaChatRequest;
|
||||
@@ -41,7 +43,7 @@ public class TestChatRequestSerialization extends AbstractSerializationTest<Olla
|
||||
|
||||
@Test
|
||||
public void testRequestWithMessageAndImage() {
|
||||
OllamaChatRequest req = builder.withMessage(OllamaChatMessageRole.USER, "Some prompt",
|
||||
OllamaChatRequest req = builder.withMessage(OllamaChatMessageRole.USER, "Some prompt", Collections.emptyList(),
|
||||
List.of(new File("src/test/resources/dog-on-a-boat.jpg"))).build();
|
||||
String jsonRequest = serialize(req);
|
||||
assertEqualsAfterUnmarshalling(deserialize(jsonRequest, OllamaChatRequest.class), req);
|
||||
@@ -59,6 +61,10 @@ public class TestChatRequestSerialization extends AbstractSerializationTest<Olla
|
||||
.withOptions(b.setSeed(1).build())
|
||||
.withOptions(b.setTopK(1).build())
|
||||
.withOptions(b.setTopP(1).build())
|
||||
.withOptions(b.setMinP(1).build())
|
||||
.withOptions(b.setCustomOption("cust_float", 1.0f).build())
|
||||
.withOptions(b.setCustomOption("cust_int", 1).build())
|
||||
.withOptions(b.setCustomOption("cust_str", "custom").build())
|
||||
.build();
|
||||
|
||||
String jsonRequest = serialize(req);
|
||||
@@ -72,6 +78,20 @@ public class TestChatRequestSerialization extends AbstractSerializationTest<Olla
|
||||
assertEquals(1, deserializeRequest.getOptions().get("seed"));
|
||||
assertEquals(1, deserializeRequest.getOptions().get("top_k"));
|
||||
assertEquals(1.0, deserializeRequest.getOptions().get("top_p"));
|
||||
assertEquals(1.0, deserializeRequest.getOptions().get("min_p"));
|
||||
assertEquals(1.0, deserializeRequest.getOptions().get("cust_float"));
|
||||
assertEquals(1, deserializeRequest.getOptions().get("cust_int"));
|
||||
assertEquals("custom", deserializeRequest.getOptions().get("cust_str"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestWithInvalidCustomOption() {
|
||||
OptionsBuilder b = new OptionsBuilder();
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> {
|
||||
OllamaChatRequest req = builder.withMessage(OllamaChatMessageRole.USER, "Some prompt")
|
||||
.withOptions(b.setCustomOption("cust_obj", new Object()).build())
|
||||
.build();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,36 +1,37 @@
|
||||
package io.github.ollama4j.unittests.jackson;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import io.github.ollama4j.models.embeddings.OllamaEmbedRequestBuilder;
|
||||
import io.github.ollama4j.models.embeddings.OllamaEmbedRequestModel;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import io.github.ollama4j.models.embeddings.OllamaEmbeddingsRequestModel;
|
||||
import io.github.ollama4j.models.embeddings.OllamaEmbeddingsRequestBuilder;
|
||||
import io.github.ollama4j.utils.OptionsBuilder;
|
||||
|
||||
public class TestEmbeddingsRequestSerialization extends AbstractSerializationTest<OllamaEmbeddingsRequestModel> {
|
||||
public class TestEmbedRequestSerialization extends AbstractSerializationTest<OllamaEmbedRequestModel> {
|
||||
|
||||
private OllamaEmbeddingsRequestBuilder builder;
|
||||
private OllamaEmbedRequestBuilder builder;
|
||||
|
||||
@BeforeEach
|
||||
public void init() {
|
||||
builder = OllamaEmbeddingsRequestBuilder.getInstance("DummyModel","DummyPrompt");
|
||||
builder = OllamaEmbedRequestBuilder.getInstance("DummyModel","DummyPrompt");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestOnlyMandatoryFields() {
|
||||
OllamaEmbeddingsRequestModel req = builder.build();
|
||||
OllamaEmbedRequestModel req = builder.build();
|
||||
String jsonRequest = serialize(req);
|
||||
assertEqualsAfterUnmarshalling(deserialize(jsonRequest,OllamaEmbeddingsRequestModel.class), req);
|
||||
assertEqualsAfterUnmarshalling(deserialize(jsonRequest,OllamaEmbedRequestModel.class), req);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestWithOptions() {
|
||||
OptionsBuilder b = new OptionsBuilder();
|
||||
OllamaEmbeddingsRequestModel req = builder
|
||||
OllamaEmbedRequestModel req = builder
|
||||
.withOptions(b.setMirostat(1).build()).build();
|
||||
|
||||
String jsonRequest = serialize(req);
|
||||
OllamaEmbeddingsRequestModel deserializeRequest = deserialize(jsonRequest,OllamaEmbeddingsRequestModel.class);
|
||||
OllamaEmbedRequestModel deserializeRequest = deserialize(jsonRequest,OllamaEmbedRequestModel.class);
|
||||
assertEqualsAfterUnmarshalling(deserializeRequest, req);
|
||||
assertEquals(1, deserializeRequest.getOptions().get("mirostat"));
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
ollama.url=http://localhost:11434
|
||||
ollama.model=qwen:0.5b
|
||||
ollama.model.image=llava
|
||||
ollama.model=llama3.2:1b
|
||||
ollama.model.image=llava:latest
|
||||
ollama.request-timeout-seconds=120
|
||||
Reference in New Issue
Block a user