Compare commits

..

50 Commits

Author SHA1 Message Date
dependabot[bot]
995fbb14ba Bump org.testcontainers:ollama from 1.20.2 to 1.21.3
Bumps [org.testcontainers:ollama](https://github.com/testcontainers/testcontainers-java) from 1.20.2 to 1.21.3.
- [Release notes](https://github.com/testcontainers/testcontainers-java/releases)
- [Changelog](https://github.com/testcontainers/testcontainers-java/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.20.2...1.21.3)

---
updated-dependencies:
- dependency-name: org.testcontainers:ollama
  dependency-version: 1.21.3
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 00:45:18 +00:00
Amith Koujalgi
b456feda64 Merge pull request #225 from ollama4j/fix-docs-build
Some checks failed
Mark stale issues / stale (push) Failing after 19s
Mark stale issues and PRs / stale (push) Failing after 51s
CodeQL / Analyze (java) (push) Failing after 12s
CodeQL / Analyze (javascript) (push) Failing after 10s
Update Docs Build GHA
2025-10-23 12:11:35 +05:30
amithkoujalgi
1c1452836d Merge branch 'main' into fix-docs-build 2025-10-23 11:56:56 +05:30
amithkoujalgi
1bca07ecb8 Add workflow_dispatch trigger to build-on-pull-request.yml for manual execution 2025-10-23 11:51:20 +05:30
amithkoujalgi
47c5943137 Update dependencies in package.json and package-lock.json to version 3.9.2 for various Docusaurus packages, including @docusaurus/core, @docusaurus/preset-classic, and @docusaurus/theme-mermaid. Also, upgrade @ai-sdk/gateway to version 2.0.0 and @algolia packages to version 5.41.0. Add @vercel/oidc package version 3.0.3. 2025-10-23 11:44:31 +05:30
amithkoujalgi
3061b2d8ef Merge branch 'main' into fix-docs-build 2025-10-23 11:19:56 +05:30
Amith Koujalgi
531f063cc9 Merge pull request #212 from ollama4j/dependabot/npm_and_yarn/docs/docsearch/js-4.2.0
Bump @docsearch/js from 4.1.0 to 4.2.0 in /docs
2025-10-23 11:19:36 +05:30
Amith Koujalgi
887a9e1bfc Merge pull request #213 from ollama4j/dependabot/maven/org.projectlombok-lombok-1.18.42
Bump org.projectlombok:lombok from 1.18.40 to 1.18.42
2025-10-23 11:19:12 +05:30
Amith Koujalgi
10e2a606b5 Merge pull request #219 from ollama4j/dependabot/npm_and_yarn/docs/docusaurus/preset-classic-3.9.2
Bump @docusaurus/preset-classic from 3.9.1 to 3.9.2 in /docs
2025-10-23 11:18:59 +05:30
Amith Koujalgi
d589315d23 Merge pull request #218 from ollama4j/dependabot/github_actions/actions/setup-node-6
Bump actions/setup-node from 5 to 6
2025-10-23 11:18:47 +05:30
Amith Koujalgi
95cc2164d3 Merge pull request #214 from ollama4j/dependabot/maven/org.junit.jupiter-junit-jupiter-api-6.0.0
Bump org.junit.jupiter:junit-jupiter-api from 5.13.4 to 6.0.0
2025-10-23 11:18:36 +05:30
Amith Koujalgi
970d54bcb5 Merge pull request #217 from ollama4j/dependabot/maven/org.sonatype.central-central-publishing-maven-plugin-0.9.0
Bump org.sonatype.central:central-publishing-maven-plugin from 0.8.0 to 0.9.0
2025-10-23 11:18:26 +05:30
Amith Koujalgi
e326936d3d Merge pull request #211 from ollama4j/dependabot/github_actions/github/codeql-action-4
Bump github/codeql-action from 3 to 4
2025-10-23 11:18:08 +05:30
Amith Koujalgi
9b916480b2 Merge pull request #216 from ollama4j/dependabot/maven/org.jacoco-jacoco-maven-plugin-0.8.14
Bump org.jacoco:jacoco-maven-plugin from 0.8.13 to 0.8.14
2025-10-23 11:17:56 +05:30
Amith Koujalgi
83d292671a Merge pull request #210 from ollama4j/snyk-upgrade-8a0a753719bdd0b263a1412cd892d136
[Snyk] Upgrade org.projectlombok:lombok from 1.18.40 to 1.18.42
2025-10-23 11:17:39 +05:30
amithkoujalgi
dcf2a0fdb6 Merge branch 'main' into fix-docs-build 2025-10-23 11:16:29 +05:30
dependabot[bot]
1ba0f02af6 Bump @docusaurus/preset-classic from 3.9.1 to 3.9.2 in /docs
Bumps [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic) from 3.9.1 to 3.9.2.
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v3.9.2/packages/docusaurus-preset-classic)

---
updated-dependencies:
- dependency-name: "@docusaurus/preset-classic"
  dependency-version: 3.9.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-23 05:46:28 +00:00
dependabot[bot]
74e6777b7c Bump @docsearch/js from 4.1.0 to 4.2.0 in /docs
Bumps [@docsearch/js](https://github.com/algolia/docsearch/tree/HEAD/packages/docsearch-js) from 4.1.0 to 4.2.0.
- [Release notes](https://github.com/algolia/docsearch/releases)
- [Changelog](https://github.com/algolia/docsearch/blob/main/CHANGELOG.md)
- [Commits](https://github.com/algolia/docsearch/commits/v4.2.0/packages/docsearch-js)

---
updated-dependencies:
- dependency-name: "@docsearch/js"
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-23 05:46:15 +00:00
Amith Koujalgi
849ae77712 Merge pull request #220 from ollama4j/dependabot/maven/ch.qos.logback-logback-classic-1.5.20
Bump ch.qos.logback:logback-classic from 1.5.18 to 1.5.20
2025-10-23 11:16:07 +05:30
Amith Koujalgi
fcd0fbe4b3 Merge pull request #221 from ollama4j/dependabot/npm_and_yarn/docs/docusaurus/plugin-google-gtag-3.9.2
Bump @docusaurus/plugin-google-gtag from 3.9.1 to 3.9.2 in /docs
2025-10-23 11:13:56 +05:30
amithkoujalgi
066df6b369 Enhance agent documentation by refining the definition and benefits of agents, improving clarity on YAML configuration parameters, and updating the sample interaction. Additionally, modify the Agent class to pull the model during initialization, ensuring proper setup for agent functionality. 2025-10-23 11:10:06 +05:30
dependabot[bot]
1fbfe8a18c Bump @docusaurus/plugin-google-gtag from 3.9.1 to 3.9.2 in /docs
Bumps [@docusaurus/plugin-google-gtag](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-plugin-google-gtag) from 3.9.1 to 3.9.2.
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v3.9.2/packages/docusaurus-plugin-google-gtag)

---
updated-dependencies:
- dependency-name: "@docusaurus/plugin-google-gtag"
  dependency-version: 3.9.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-22 12:04:31 +00:00
Amith Koujalgi
23785bb5b7 Merge pull request #222 from ollama4j/dependabot/npm_and_yarn/docs/docusaurus/core-3.9.2
Some checks failed
CodeQL / Analyze (java) (push) Failing after 11s
CodeQL / Analyze (javascript) (push) Failing after 8s
Mark stale issues / stale (push) Failing after 12s
Mark stale issues and PRs / stale (push) Failing after 10s
Bump @docusaurus/core from 3.9.1 to 3.9.2 in /docs
2025-10-22 17:33:09 +05:30
dependabot[bot]
00cd2a0adf Bump @docusaurus/core from 3.9.1 to 3.9.2 in /docs
Bumps [@docusaurus/core](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus) from 3.9.1 to 3.9.2.
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v3.9.2/packages/docusaurus)

---
updated-dependencies:
- dependency-name: "@docusaurus/core"
  dependency-version: 3.9.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-22 07:34:56 +00:00
Amith Koujalgi
49fac9d2cf Merge pull request #223 from ollama4j/dependabot/npm_and_yarn/docs/docusaurus/module-type-aliases-3.9.2
Some checks failed
CodeQL / Analyze (java) (push) Failing after 8s
CodeQL / Analyze (javascript) (push) Failing after 6s
Bump @docusaurus/module-type-aliases from 3.9.1 to 3.9.2 in /docs
2025-10-22 13:03:22 +05:30
amithkoujalgi
ca1a73fa76 Improved agent documentation 2025-10-22 13:01:19 +05:30
amithkoujalgi
adbf6a8185 Enhance agent documentation by adding interactive examples using the TypewriterTextarea component. Update the content to demonstrate sample interactions, improving clarity and user engagement. 2025-10-22 12:08:52 +05:30
amithkoujalgi
57adaafb42 Refactor Javadoc comments in Agent class for consistency and clarity, consolidating multi-line comments into single-line format. Update interact method to return chat history instead of a string response, enhancing functionality and documentation. 2025-10-21 10:35:06 +05:30
amithkoujalgi
ad03c784e5 Clarify Tool Functions section in agent documentation by removing redundant text in YAML registration instructions for Java classes. 2025-10-20 23:37:53 +05:30
amithkoujalgi
2e245a0e16 Implement Tool Functions section in agent documentation, detailing how to register Java classes for agent functionality. Update YAML configuration instructions accordingly. 2025-10-20 23:36:29 +05:30
amithkoujalgi
7c0c4e38ed Update Maven configuration to disable error failures and modify GitHub Actions workflow to skip GPG signing and tests during the build process. 2025-10-20 22:57:42 +05:30
amithkoujalgi
a0c1184e7b Merge remote-tracking branch 'origin/main'
Some checks failed
CodeQL / Analyze (javascript) (push) Failing after 8s
CodeQL / Analyze (java) (push) Failing after 10s
Mark stale issues / stale (push) Failing after 12s
Mark stale issues and PRs / stale (push) Failing after 29s
2025-10-20 22:49:45 +05:30
Amith Koujalgi
8c485edb32 Merge pull request #224 from ollama4j/feature/agent
Added a new feature - `agents`
2025-10-20 22:41:34 +05:30
amithkoujalgi
7ce89a3e89 Update Javadoc in Agent class to reflect changes in the interact method signature, now including an OllamaChatStreamObserver parameter for improved conversation handling. 2025-10-20 22:31:04 +05:30
Amith Koujalgi
fe43e87e1a Enhance agent documentation with detailed YAML configuration instructions and benefits. Update CodeEmbed component to support customizable language for syntax highlighting. Refactor Agent class to improve Javadoc comments and method signatures for better clarity and functionality. 2025-10-20 20:07:15 +05:30
dependabot[bot]
f49c6d162a Bump @docusaurus/module-type-aliases from 3.9.1 to 3.9.2 in /docs
Bumps [@docusaurus/module-type-aliases](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-module-type-aliases) from 3.9.1 to 3.9.2.
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v3.9.2/packages/docusaurus-module-type-aliases)

---
updated-dependencies:
- dependency-name: "@docusaurus/module-type-aliases"
  dependency-version: 3.9.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-20 00:50:44 +00:00
dependabot[bot]
751f2585b4 Bump ch.qos.logback:logback-classic from 1.5.18 to 1.5.20
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.18 to 1.5.20.
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.18...v_1.5.20)

---
updated-dependencies:
- dependency-name: ch.qos.logback:logback-classic
  dependency-version: 1.5.20
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-20 00:48:33 +00:00
dependabot[bot]
a4edcf4e43 Bump actions/setup-node from 5 to 6
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 5 to 6.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-20 00:47:32 +00:00
Amith Koujalgi
d55d1c0fd9 Update Javadoc comments in Ollama and Agent classes to reflect correct method references for chat request construction and agent instantiation. 2025-10-19 14:34:21 +05:30
Amith Koujalgi
f0e5a9e172 Add documentation for the new Agent feature and update sidebar positions for Metrics and API categories. Adjust code examples in various API documentation to reflect correct paths and improve clarity. Enhance the Agent class with an equals and hash code method for better functionality. 2025-10-19 14:03:10 +05:30
amithkoujalgi
866c08f590 Remove SampleAgent class and associated YAML configuration file, streamlining the project by eliminating example implementations and their dependencies. 2025-10-19 11:22:16 +05:30
amithkoujalgi
bec634dd37 Refactor Agent class to include request timeout configuration and enhance interactive input display. Remove commented-out code for clarity. Update SampleAgent to utilize YAML configuration for agent instantiation. 2025-10-18 20:19:43 +05:30
amithkoujalgi
cbf65fef48 Add YAML support for Agent configuration and enhance Agent class to load tools from YAML. Introduce custom prompt functionality and refactor constructor to accept additional parameters. Update SampleAgent to demonstrate YAML loading. 2025-10-13 14:34:03 +05:30
dependabot[bot]
eee8fe5755 Bump org.sonatype.central:central-publishing-maven-plugin
Bumps [org.sonatype.central:central-publishing-maven-plugin](https://github.com/sonatype/central-publishing-maven-plugin) from 0.8.0 to 0.9.0.
- [Commits](https://github.com/sonatype/central-publishing-maven-plugin/commits)

---
updated-dependencies:
- dependency-name: org.sonatype.central:central-publishing-maven-plugin
  dependency-version: 0.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-13 00:42:34 +00:00
dependabot[bot]
a7a030b9b0 Bump org.jacoco:jacoco-maven-plugin from 0.8.13 to 0.8.14
Bumps [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.13 to 0.8.14.
- [Release notes](https://github.com/jacoco/jacoco/releases)
- [Commits](https://github.com/jacoco/jacoco/compare/v0.8.13...v0.8.14)

---
updated-dependencies:
- dependency-name: org.jacoco:jacoco-maven-plugin
  dependency-version: 0.8.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-13 00:42:20 +00:00
dependabot[bot]
e793320f7c Bump org.junit.jupiter:junit-jupiter-api from 5.13.4 to 6.0.0
Bumps [org.junit.jupiter:junit-jupiter-api](https://github.com/junit-team/junit-framework) from 5.13.4 to 6.0.0.
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](https://github.com/junit-team/junit-framework/compare/r5.13.4...r6.0.0)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter-api
  dependency-version: 6.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-13 00:41:21 +00:00
dependabot[bot]
4c3cf3b335 Bump org.projectlombok:lombok from 1.18.40 to 1.18.42
Bumps [org.projectlombok:lombok](https://github.com/projectlombok/lombok) from 1.18.40 to 1.18.42.
- [Changelog](https://github.com/projectlombok/lombok/blob/master/doc/changelog.markdown)
- [Commits](https://github.com/projectlombok/lombok/compare/v1.18.40...v1.18.42)

---
updated-dependencies:
- dependency-name: org.projectlombok:lombok
  dependency-version: 1.18.42
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-13 00:40:58 +00:00
dependabot[bot]
ecfbc1b394 Bump github/codeql-action from 3 to 4
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-13 00:32:45 +00:00
amithkoujalgi
6df57c4a23 Enhance SampleAgent with improved Javadoc comments for tool specifications and descriptions, clarifying usage and parameters for weather, calculator, and hotel booking tools. 2025-10-11 00:00:42 +05:30
amithkoujalgi
da6d20d118 Add default target to Makefile, enhance Ollama class to use tools, and introduce Agent and SampleAgent classes for interactive tool usage. Update Javadoc generation message and improve error handling in endpoint callers. 2025-10-10 23:56:31 +05:30
24 changed files with 899 additions and 511 deletions

View File

@@ -8,6 +8,8 @@ on:
paths:
- 'src/**'
- 'pom.xml'
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
@@ -52,7 +54,7 @@ jobs:
steps:
- uses: actions/checkout@v5
- name: Use Node.js
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version: '20.x'
- run: cd docs && npm ci

View File

@@ -32,13 +32,13 @@ jobs:
java-version: '21'
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v3
uses: github/codeql-action/autobuild@v4
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4

View File

@@ -40,7 +40,7 @@ jobs:
- uses: actions/checkout@v5
- name: Use Node.js
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version: '20.x'
- run: cd docs && npm ci
@@ -54,7 +54,7 @@ jobs:
regex: false
- name: Build with Maven
run: mvn --file pom.xml -U clean package && cp -r ./target/apidocs/. ./docs/build/apidocs
run: mvn --file pom.xml -U clean package -Dgpg.skip=true -DskipTests && cp -r ./target/apidocs/. ./docs/build/apidocs
- name: Doxygen Action
uses: mattnotmitt/doxygen-action@v1.12.0

View File

@@ -1,3 +1,7 @@
# Default target
.PHONY: all
all: dev build
dev:
@echo "Setting up dev environment..."
@command -v pre-commit >/dev/null 2>&1 || { echo "Error: pre-commit is not installed. Please install it first."; exit 1; }
@@ -43,7 +47,7 @@ doxygen:
@doxygen Doxyfile
javadoc:
@echo "\033[0;34mGenerating Javadocs into '$(javadocfolder)'...\033[0m"
@echo "\033[0;34mGenerating Javadocs...\033[0m"
@mvn clean javadoc:javadoc
@if [ -f "target/reports/apidocs/index.html" ]; then \
echo "\033[0;32mJavadocs generated in target/reports/apidocs/index.html\033[0m"; \

95
docs/docs/agent.md Normal file
View File

@@ -0,0 +1,95 @@
---
sidebar_position: 4
title: Agents 🆕
---
import CodeEmbed from '@site/src/components/CodeEmbed';
import TypewriterTextarea from '@site/src/components/TypewriterTextarea';
# Agents
An **agent** is an intelligent assistant that understands user requests, communicates using LLMs, and performs actions by invoking appropriate tools (exposed as code).
With agents, you can:
- Orchestrate multi-step reasoning and tool use (e.g., answering questions, looking up data, making reservations, sending emails, and more)
- Automatically select and execute the right tools or actions based on user intent
- Maintain conversation context to support dynamic, interactive problem solving
- Adapt behavior, persona, or expertise by simply changing configuration—without changing your Java code
Agents help by acting as an intelligent bridge between users, LLMs, and your application's capabilities. They can automate tasks, provide personalized assistance, and extend what LLMs can do by calling your Java methods or integrating with external systems.
With Ollama4j, creating an agent is as simple as describing its purpose, available tools, behavior, and preferred language model—all defined in a single YAML file.
**Why consider building agents using Ollama4j?**
- **Seamless Customization:** Effortlessly fine-tune your agent's personality, expertise, or workflow by editing the YAML—no need to recompile or modify your Java code.
- **Plug-and-Play Extensibility:** Add new tools or swap out existing logic classes without wrestling with framework internals or glue code.
- **Rapid Iteration:** Experiment freely. Try different models, instructions, and toolsets to try new behaviors or orchestrations in minutes.
- **Clear Separation of Concerns:** Keep your core business logic (Java) and conversational configuration (YAML) distinct, promoting clarity, maintainability, and collaboration.
---
### Define an Agent in YAML
Specify everything about your agent—what LLM it uses, its “personality,” and all callable tools—in a single YAML file.
**Agent configuration parameters:**
| Field | Description |
|-------------------------|------------------------------------------------------------------------------------------------|
| `name` | Name of your agent. |
| `host` | The base URL for your Ollama server (e.g., `http://localhost:11434`). |
| `model` | The LLM backing your agent (e.g., `llama3`, `gemma`, `mistral`, etc). |
| `customPrompt` | _(optional)_ System prompt—instructions or persona for your agent. |
| `tools` | List of tools the agent can use. Each tool entry describes the name, function, and parameters. |
| `toolFunctionFQCN` | Fully qualified Java class name implementing the tool logic. Must be present on classpath. |
| `requestTimeoutSeconds` | _(optional)_ How long (seconds) to wait for agent replies. |
YAML makes it effortless to configure and tweak your agents powers and behavior—no code changes needed!
**Example agent YAML:**
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/resources/agent.yaml" language='yaml'/>
---
### Implement Tool Functions
Your agent calls out to Java classes (Tool Functions). Put these implementations on your classpath, register them in YAML.
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/tools/toolfunctions/HotelBookingLookupToolFunction.java"/>
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/tools/toolfunctions/HotelBookingToolFunction.java"/>
---
### Instantiating and Running Agents
Once your agent is described in YAML, bringing it to life in Java takes only a couple of lines:
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/AgentExample.java"/>
The API takes care of wiring up LLMs, tool invocation, and instruction handling.
Here's a sample interaction:
<TypewriterTextarea
textContent='[You]: Book a hotel in Mysuru for two guests, from July 20 to July 22.
Alright, I have booked the hotel! Room number 10 booked for 2 guests in Mysuru from July 20th to July 22nd. Here is your booking ID: HB-123'
typingSpeed={30}
pauseBetweenSentences={1200}
height='110px'
width='100%'
/>
Here's another one:
<TypewriterTextarea
textContent='[You]: Give me details of booking ID - HB-123.
I found a booking for HB-123. Looks like the hotel is booked for 2 guests. Enjoy your stay!'
typingSpeed={30}
pauseBetweenSentences={1200}
height='90px'
width='100%'
/>

View File

@@ -1,6 +1,6 @@
{
"label": "APIs - Extras",
"position": 4,
"label": "Extras",
"position": 5,
"link": {
"type": "generated-index",
"description": "Details of APIs to handle bunch of extra stuff."

View File

@@ -1,5 +1,5 @@
{
"label": "APIs - Generate",
"label": "Generate",
"position": 3,
"link": {
"type": "generated-index",

View File

@@ -66,11 +66,11 @@ To use a method as a tool within a chat call, follow these steps:
Let's try an example. Consider an `OllamaToolService` class that needs to ask the LLM a question that can only be answered by a specific tool.
This tool is implemented within a `GlobalConstantGenerator` class. Following is the code that exposes an annotated method as a tool:
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/toolcalling/annotated/GlobalConstantGenerator.java"/>
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/tools/annotated/GlobalConstantGenerator.java"/>
The annotated method can then be used as a tool in the chat session:
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/toolcalling/annotated/AnnotatedToolCallingExample.java"/>
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/AnnotatedToolCallingExample.java"/>
Running the above would produce a response similar to:

View File

@@ -63,7 +63,7 @@ You will get a response similar to:
### Using a simple Console Output Stream Handler
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/ConsoleOutputStreamHandlerExample.java" />
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/ChatWithConsoleHandlerExample.java" />
### With a Stream Handler to receive the tokens as they are generated

View File

@@ -19,11 +19,11 @@ You can use this feature to receive both the thinking and the response as separa
You will get a response similar to:
:::tip[Thinking Tokens]
User asks "Who are you?" It's a request for identity. As ChatGPT, we should explain that I'm an AI developed by OpenAI, etc. Provide friendly explanation.
USER ASKS "WHO ARE YOU?" IT'S A REQUEST FOR IDENTITY. AS CHATGPT, WE SHOULD EXPLAIN THAT I'M AN AI DEVELOPED BY OPENAI, ETC. PROVIDE FRIENDLY EXPLANATION.
:::
:::tip[Response Tokens]
Im ChatGPT, a large language model created by OpenAI. Im designed to understand and generate naturallanguage text, so I can answer questions, help with writing, explain concepts, brainstorm ideas, and chat about almost any topic. I dont have a personal life or consciousness—Im a tool that processes input and produces responses based on patterns in the data I was trained on. If you have any questions about how I work or what I can do, feel free to ask!
im chatgpt, a large language model created by openai. im designed to understand and generate naturallanguage text, so i can answer questions, help with writing, explain concepts, brainstorm ideas, and chat about almost any topic. i dont have a personal life or consciousness—im a tool that processes input and produces responses based on patterns in the data i was trained on. if you have any questions about how i work or what i can do, feel free to ask!
:::
### Generate response and receive the thinking and response tokens streamed
@@ -34,7 +34,7 @@ You will get a response similar to:
:::tip[Thinking Tokens]
<TypewriterTextarea
textContent={`User asks "Who are you?" It's a request for identity. As ChatGPT, we should explain that I'm an AI developed by OpenAI, etc. Provide friendly explanation.`}
textContent={`USER ASKS "WHO ARE YOU?" WE SHOULD EXPLAIN THAT I'M AN AI BY OPENAI, ETC.`}
typingSpeed={10}
pauseBetweenSentences={1200}
height="auto"
@@ -45,7 +45,7 @@ style={{ whiteSpace: 'pre-line' }}
:::tip[Response Tokens]
<TypewriterTextarea
textContent={`Im ChatGPT, a large language model created by OpenAI. Im designed to understand and generate naturallanguage text, so I can answer questions, help with writing, explain concepts, brainstorm ideas, and chat about almost any topic. I dont have a personal life or consciousness—Im a tool that processes input and produces responses based on patterns in the data I was trained on. If you have any questions about how I work or what I can do, feel free to ask!`}
textContent={`im chatgpt, a large language model created by openai.`}
typingSpeed={10}
pauseBetweenSentences={1200}
height="auto"

View File

@@ -3,6 +3,7 @@ sidebar_position: 4
---
import CodeEmbed from '@site/src/components/CodeEmbed';
import TypewriterTextarea from '@site/src/components/TypewriterTextarea';
# Generate with Images
@@ -17,13 +18,11 @@ recommended.
:::
## Synchronous mode
If you have this image downloaded and you pass the path to the downloaded image to the following code:
![Img](https://t3.ftcdn.net/jpg/02/96/63/80/360_F_296638053_0gUVA4WVBKceGsIr7LNqRWSnkusi07dq.jpg)
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/GenerateWithImageFile.java" />
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/GenerateWithImageFileSimple.java" />
You will get a response similar to:
@@ -32,30 +31,22 @@ This image features a white boat with brown cushions, where a dog is sitting on
be enjoying its time outdoors, perhaps on a lake.
:::
# Generate with Image URLs
This API lets you ask questions along with the image files to the LLMs.
This API corresponds to
the [completion](https://github.com/jmorganca/ollama/blob/main/docs/api.md#generate-a-completion) API.
:::note
Executing this on Ollama server running in CPU-mode will take longer to generate response. Hence, GPU-mode is
recommended.
:::
## Ask (Sync)
Passing the link of this image the following code:
If you want the response to be streamed, you can use the following code:
![Img](https://t3.ftcdn.net/jpg/02/96/63/80/360_F_296638053_0gUVA4WVBKceGsIr7LNqRWSnkusi07dq.jpg)
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/GenerateWithImageURL.java" />
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/GenerateWithImageFileStreaming.java" />
You will get a response similar to:
:::tip[LLM Response]
This image features a white boat with brown cushions, where a dog is sitting on the back of the boat. The dog seems to
be enjoying its time outdoors, perhaps on a lake.
:::tip[Response Tokens]
<TypewriterTextarea
textContent={`This image features a white boat with brown cushions, where a dog is sitting on the back of the boat. The dog seems to be enjoying its time outdoors, perhaps on a lake.`}
typingSpeed={10}
pauseBetweenSentences={1200}
height="auto"
width="100%"
style={{ whiteSpace: 'pre-line' }}
/>
:::

View File

@@ -36,19 +36,19 @@ We can create static functions as our tools.
This function takes the arguments `location` and `fuelType` and performs an operation with these arguments and returns
fuel price value.
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/toolcalling/tools/FuelPriceTool.java"/ >
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/tools/toolfunctions/FuelPriceToolFunction.java"/ >
This function takes the argument `city` and performs an operation with the argument and returns the weather for a
location.
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/toolcalling/tools/WeatherTool.java"/ >
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/tools/toolfunctions/WeatherToolFunction.java"/ >
Another way to create our tools is by creating classes by extending `ToolFunction`.
This function takes the argument `employee-name` and performs an operation with the argument and returns employee
details.
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/toolcalling/tools/DBQueryFunction.java"/ >
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/tools/toolfunctions/EmployeeFinderToolFunction.java"/ >
### Define Tool Specifications
@@ -57,21 +57,21 @@ Lets define a sample tool specification called **Fuel Price Tool** for getting t
- Specify the function `name`, `description`, and `required` properties (`location` and `fuelType`).
- Associate the `getCurrentFuelPrice` function you defined earlier.
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/toolcalling/toolspecs/FuelPriceToolSpec.java"/ >
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/tools/toolspecs/FuelPriceToolSpec.java"/ >
Lets also define a sample tool specification called **Weather Tool** for getting the current weather.
- Specify the function `name`, `description`, and `required` property (`city`).
- Associate the `getCurrentWeather` function you defined earlier.
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/toolcalling/toolspecs/WeatherToolSpec.java"/ >
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/tools/toolspecs/WeatherToolSpec.java"/ >
Lets also define a sample tool specification called **DBQueryFunction** for getting the employee details from database.
- Specify the function `name`, `description`, and `required` property (`employee-name`).
- Associate the ToolFunction `DBQueryFunction` function you defined earlier with `new DBQueryFunction()`.
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/toolcalling/toolspecs/DatabaseQueryToolSpec.java"/ >
<CodeEmbed src="https://raw.githubusercontent.com/ollama4j/ollama4j-examples/refs/heads/main/src/main/java/io/github/ollama4j/examples/tools/toolspecs/EmployeeFinderToolSpec.java"/ >
Now put it all together by registering the tools and prompting with tools.

View File

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

View File

@@ -15,13 +15,13 @@ This API lets you create a custom model on the Ollama server.
You would see these logs while the custom model is being created:
```
{"status":"using existing layer sha256:fad2a06e4cc705c2fa8bec5477ddb00dc0c859ac184c34dcc5586663774161ca"}
{"status":"using existing layer sha256:41c2cf8c272f6fb0080a97cd9d9bd7d4604072b80a0b10e7d65ca26ef5000c0c"}
{"status":"using existing layer sha256:1da0581fd4ce92dcf5a66b1da737cf215d8dcf25aa1b98b44443aaf7173155f5"}
{"status":"creating new layer sha256:941b69ca7dc2a85c053c38d9e8029c9df6224e545060954fa97587f87c044a64"}
{"status":"using existing layer sha256:f02dd72bb2423204352eabc5637b44d79d17f109fdb510a7c51455892aa2d216"}
{"status":"writing manifest"}
{"status":"success"}
using existing layer sha256:fad2a06e4cc705c2fa8bec5477ddb00dc0c859ac184c34dcc5586663774161ca
using existing layer sha256:41c2cf8c272f6fb0080a97cd9d9bd7d4604072b80a0b10e7d65ca26ef5000c0c
using existing layer sha256:1da0581fd4ce92dcf5a66b1da737cf215d8dcf25aa1b98b44443aaf7173155f5
creating new layer sha256:941b69ca7dc2a85c053c38d9e8029c9df6224e545060954fa97587f87c044a64
using existing layer sha256:f02dd72bb2423204352eabc5637b44d79d17f109fdb510a7c51455892aa2d216
writing manifest
success
```
Once created, you can see it when you use [list models](./list-models) API.

View File

@@ -1,7 +1,7 @@
---
sidebar_position: 5
sidebar_position: 6
title: Metrics
title: Metrics 🆕
---
import CodeEmbed from '@site/src/components/CodeEmbed';

695
docs/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -15,10 +15,11 @@
},
"dependencies": {
"@docsearch/js": "^4.1.0",
"@docusaurus/core": "^3.9.0",
"@docusaurus/plugin-google-gtag": "^3.9.1",
"@docusaurus/preset-classic": "^3.9.1",
"@docusaurus/theme-mermaid": "^3.9.1",
"@docusaurus/core": "^3.9.2",
"@docusaurus/plugin-google-gtag": "^3.9.2",
"@docusaurus/preset-classic": "^3.9.2",
"@docusaurus/theme-mermaid": "^3.9.2",
"@docusaurus/plugin-content-docs": "^3.9.2",
"@iconify/react": "^6.0.2",
"@mdx-js/react": "^3.1.1",
"clsx": "^2.1.1",
@@ -30,8 +31,8 @@
"react-image-gallery": "^1.4.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.9.1",
"@docusaurus/types": "^3.4.0"
"@docusaurus/module-type-aliases": "^3.9.2",
"@docusaurus/types": "^3.9.2"
},
"browserslist": {
"production": [

View File

@@ -1,84 +1,14 @@
// import React, { useState, useEffect } from 'react';
// import CodeBlock from '@theme/CodeBlock';
// import Icon from '@site/src/components/Icon';
// const CodeEmbed = ({ src }) => {
// const [code, setCode] = useState('');
// const [loading, setLoading] = useState(true);
// const [error, setError] = useState(null);
// useEffect(() => {
// let isMounted = true;
// const fetchCodeFromUrl = async (url) => {
// if (!isMounted) return;
// setLoading(true);
// setError(null);
// try {
// const response = await fetch(url);
// if (!response.ok) {
// throw new Error(`HTTP error! status: ${response.status}`);
// }
// const data = await response.text();
// if (isMounted) {
// setCode(data);
// }
// } catch (err) {
// console.error('Failed to fetch code:', err);
// if (isMounted) {
// setError(err);
// setCode(`// Failed to load code from ${url}\n// ${err.message}`);
// }
// } finally {
// if (isMounted) {
// setLoading(false);
// }
// }
// };
// if (src) {
// fetchCodeFromUrl(src);
// }
// return () => {
// isMounted = false;
// };
// }, [src]);
// const githubUrl = src ? src.replace('https://raw.githubusercontent.com', 'https://github.com').replace('/refs/heads/', '/blob/') : null;
// const fileName = src ? src.substring(src.lastIndexOf('/') + 1) : null;
// return (
// loading ? (
// <div>Loading code...</div>
// ) : error ? (
// <div>Error: {error.message}</div>
// ) : (
// <div style={{ backgroundColor: 'transparent', padding: '0px', borderRadius: '5px' }}>
// <div style={{ textAlign: 'right' }}>
// {githubUrl && (
// <a href={githubUrl} target="_blank" rel="noopener noreferrer" style={{ paddingRight: '15px', color: 'gray', fontSize: '0.8em', fontStyle: 'italic', display: 'inline-flex', alignItems: 'center' }}>
// View on GitHub
// <Icon icon="mdi:github" height="48" />
// </a>
// )}
// </div>
// <CodeBlock title={fileName} className="language-java">{code}</CodeBlock>
// </div>
// )
// );
// };
// export default CodeEmbed;
import React, { useState, useEffect } from 'react';
import React, {useState, useEffect} from 'react';
import CodeBlock from '@theme/CodeBlock';
import Icon from '@site/src/components/Icon';
const CodeEmbed = ({ src }) => {
/**
* CodeEmbed component to display code fetched from a URL in a CodeBlock.
* @param {object} props
* @param {string} props.src - Source URL to fetch the code from.
* @param {string} [props.language='java'] - Language for syntax highlighting in CodeBlock.
*/
const CodeEmbed = ({src, language = 'java'}) => {
const [code, setCode] = useState('');
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
@@ -127,7 +57,7 @@ const CodeEmbed = ({ src }) => {
const fileName = src ? src.substring(src.lastIndexOf('/') + 1) : null;
const title = (
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
<a
href={githubUrl}
target="_blank"
@@ -146,9 +76,15 @@ const CodeEmbed = ({ src }) => {
<span>{fileName}</span>
</a>
{githubUrl && (
<a href={githubUrl} target="_blank" rel="noopener noreferrer" style={{ color: 'gray', fontSize: '0.9em', fontStyle: 'italic', display: 'inline-flex', alignItems: 'center' }}>
<a href={githubUrl} target="_blank" rel="noopener noreferrer" style={{
color: 'gray',
fontSize: '0.9em',
fontStyle: 'italic',
display: 'inline-flex',
alignItems: 'center'
}}>
View on GitHub
<Icon icon="mdi:github" height="1em" />
<Icon icon="mdi:github" height="1em"/>
</a>
)}
</div>
@@ -160,8 +96,8 @@ const CodeEmbed = ({ src }) => {
) : error ? (
<div>Error: {error.message}</div>
) : (
<div style={{ backgroundColor: 'transparent', padding: '0px', borderRadius: '5px' }}>
<CodeBlock title={title} className="language-java">{code}</CodeBlock>
<div style={{backgroundColor: 'transparent', padding: '0px', borderRadius: '5px'}}>
<CodeBlock title={title} language={language}>{code}</CodeBlock>
</div>
)
);

22
pom.xml
View File

@@ -80,6 +80,7 @@
<configuration>
<!-- to disable the "missing" warnings. Remove the doclint to enable warnings-->
<doclint>all,-missing</doclint>
<failOnError>false</failOnError>
</configuration>
<executions>
<execution>
@@ -259,6 +260,11 @@
<artifactId>jackson-databind</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
@@ -267,7 +273,7 @@
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.18</version>
<version>1.5.20</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -275,11 +281,10 @@
<artifactId>slf4j-api</artifactId>
<version>2.0.17</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.13.4</version>
<version>6.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -294,11 +299,10 @@
<version>20250517</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>ollama</artifactId>
<version>1.20.2</version>
<version>1.21.3</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -307,14 +311,12 @@
<version>1.21.3</version>
<scope>test</scope>
</dependency>
<!-- Prometheus metrics dependencies -->
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient</artifactId>
<version>0.16.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
@@ -345,7 +347,7 @@
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>0.8.0</version>
<version>0.9.0</version>
<extensions>true</extensions>
<configuration>
<publishingServerId>mvn-repo-id</publishingServerId>
@@ -371,7 +373,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.13</version>
<version>0.8.14</version>
<executions>
<execution>
<goals>
@@ -482,7 +484,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.13</version>
<version>0.8.14</version>
<executions>
<execution>
<goals>

View File

@@ -805,6 +805,7 @@ public class Ollama {
chatRequest.setMessages(msgs);
msgs.add(ocm);
OllamaChatTokenHandler hdlr = null;
chatRequest.setUseTools(true);
chatRequest.setTools(request.getTools());
if (streamObserver != null) {
chatRequest.setStream(true);
@@ -861,7 +862,7 @@ public class Ollama {
/**
* Sends a chat request to a model using an {@link OllamaChatRequest} and sets up streaming response.
* This can be constructed using an {@link OllamaChatRequestBuilder}.
* This can be constructed using an {@link OllamaChatRequest#builder()}.
*
* <p>Note: the OllamaChatRequestModel#getStream() property is not implemented.
*
@@ -881,7 +882,7 @@ public class Ollama {
// only add tools if tools flag is set
if (request.isUseTools()) {
// add all registered tools to request
request.setTools(toolRegistry.getRegisteredTools());
request.getTools().addAll(toolRegistry.getRegisteredTools());
}
if (tokenHandler != null) {

View File

@@ -0,0 +1,290 @@
/*
* Ollama4j - Java library for interacting with Ollama server.
* Copyright (c) 2025 Amith Koujalgi and contributors.
*
* Licensed under the MIT License (the "License");
* you may not use this file except in compliance with the License.
*
*/
package io.github.ollama4j.agent;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import io.github.ollama4j.Ollama;
import io.github.ollama4j.exceptions.OllamaException;
import io.github.ollama4j.impl.ConsoleOutputGenerateTokenHandler;
import io.github.ollama4j.models.chat.*;
import io.github.ollama4j.tools.ToolFunction;
import io.github.ollama4j.tools.Tools;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import lombok.*;
/**
* The {@code Agent} class represents an AI assistant capable of interacting with the Ollama API
* server.
*
* <p>It supports the use of tools (interchangeable code components), persistent chat history, and
* interactive as well as pre-scripted chat sessions.
*
* <h2>Usage</h2>
*
* <ul>
* <li>Instantiate an Agent via {@link #load(String)} for YAML-based configuration.
* <li>Handle conversation turns via {@link #interact(String, OllamaChatStreamObserver)}.
* <li>Use {@link #runInteractive()} for an interactive console-based session.
* </ul>
*/
public class Agent {
/** The agent's display name */
private final String name;
/** List of supported tools for this agent */
private final List<Tools.Tool> tools;
/** Ollama client instance for communication with the API */
private final Ollama ollamaClient;
/** The model name used for chat completions */
private final String model;
/** Persists chat message history across rounds */
private final List<OllamaChatMessage> chatHistory;
/** Optional custom system prompt for the agent */
private final String customPrompt;
/**
* Constructs a new Agent.
*
* @param name The agent's given name.
* @param ollamaClient The Ollama API client instance to use.
* @param model The model name to use for chat completion.
* @param customPrompt A custom prompt to prepend to all conversations (may be null).
* @param tools List of available tools for function calling.
*/
public Agent(
String name,
Ollama ollamaClient,
String model,
String customPrompt,
List<Tools.Tool> tools) {
this.name = name;
this.ollamaClient = ollamaClient;
this.chatHistory = new ArrayList<>();
this.tools = tools;
this.model = model;
this.customPrompt = customPrompt;
}
/**
* Loads and constructs an Agent from a YAML configuration file (classpath or filesystem).
*
* <p>The YAML should define the agent, the model, and the desired tool functions (using their
* fully qualified class names for auto-discovery).
*
* @param yamlPathOrResource Path or classpath resource name of the YAML file.
* @return New Agent instance loaded according to the YAML definition.
* @throws RuntimeException if the YAML cannot be read or agent cannot be constructed.
*/
public static Agent load(String yamlPathOrResource) {
try {
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
InputStream input =
Agent.class.getClassLoader().getResourceAsStream(yamlPathOrResource);
if (input == null) {
java.nio.file.Path filePath = java.nio.file.Paths.get(yamlPathOrResource);
if (java.nio.file.Files.exists(filePath)) {
input = java.nio.file.Files.newInputStream(filePath);
} else {
throw new RuntimeException(
yamlPathOrResource + " not found in classpath or file system");
}
}
AgentSpec agentSpec = mapper.readValue(input, AgentSpec.class);
List<AgentToolSpec> tools = agentSpec.getTools();
for (AgentToolSpec tool : tools) {
String fqcn = tool.getToolFunctionFQCN();
if (fqcn != null && !fqcn.isEmpty()) {
try {
Class<?> clazz = Class.forName(fqcn);
Object instance = clazz.getDeclaredConstructor().newInstance();
if (instance instanceof ToolFunction) {
tool.setToolFunctionInstance((ToolFunction) instance);
} else {
throw new RuntimeException(
"Class does not implement ToolFunction: " + fqcn);
}
} catch (Exception e) {
throw new RuntimeException(
"Failed to instantiate tool function: " + fqcn, e);
}
}
}
List<Tools.Tool> agentTools = new ArrayList<>();
for (AgentToolSpec a : tools) {
Tools.Tool t = new Tools.Tool();
t.setToolFunction(a.getToolFunctionInstance());
Tools.ToolSpec ts = new Tools.ToolSpec();
ts.setName(a.getName());
ts.setDescription(a.getDescription());
ts.setParameters(a.getParameters());
t.setToolSpec(ts);
agentTools.add(t);
}
Ollama ollama = new Ollama(agentSpec.getHost());
ollama.setRequestTimeoutSeconds(120);
ollama.pullModel(agentSpec.getModel());
return new Agent(
agentSpec.getName(),
ollama,
agentSpec.getModel(),
agentSpec.getCustomPrompt(),
agentTools);
} catch (Exception e) {
throw new RuntimeException("Failed to load agent from YAML", e);
}
}
/**
* Conducts a conversational interaction with the agent.
*
* @param userInput the user's question, instruction, or message for the agent.
* @param chatTokenHandler an optional handler for receiving streaming token updates from the model as it generates a reply.
* Can be {@code null} if streaming output is not needed.
* @return Updated chat history, as a list of {@link OllamaChatMessage} objects representing the complete conversation so far.
* This includes system, user, assistant, and any tool/function calls/results.
* @throws OllamaException if an error occurs communicating with the Ollama API or running tools.
*/
public List<OllamaChatMessage> interact(
String userInput, OllamaChatStreamObserver chatTokenHandler) throws OllamaException {
// Build a concise and readable description of available tools
String availableToolsDescription =
tools.isEmpty()
? ""
: tools.stream()
.map(
t ->
String.format(
"- %s: %s",
t.getToolSpec().getName(),
t.getToolSpec().getDescription() != null
? t.getToolSpec().getDescription()
: "No description"))
.reduce((a, b) -> a + "\n" + b)
.map(desc -> "\nYou have access to the following tools:\n" + desc)
.orElse("");
// Add system prompt if chatHistory is empty
if (chatHistory.isEmpty()) {
String systemPrompt =
String.format(
"You are a helpful AI assistant named %s. Your actions are limited to"
+ " using the available tools. %s%s",
name,
(customPrompt != null ? customPrompt : ""),
availableToolsDescription);
chatHistory.add(new OllamaChatMessage(OllamaChatMessageRole.SYSTEM, systemPrompt));
}
// Add the user input as a message before sending request
chatHistory.add(new OllamaChatMessage(OllamaChatMessageRole.USER, userInput));
OllamaChatRequest request =
OllamaChatRequest.builder()
.withTools(tools)
.withUseTools(true)
.withModel(model)
.withMessages(chatHistory)
.build();
OllamaChatResult response = ollamaClient.chat(request, chatTokenHandler);
chatHistory.clear();
chatHistory.addAll(response.getChatHistory());
return response.getChatHistory();
}
/**
* Launches an endless interactive console session with the agent, echoing user input and the
* agent's response using the provided chat model and tools.
*
* <p>Type {@code exit} to break the loop and terminate the session.
*
* @throws OllamaException if any errors occur talking to the Ollama API.
*/
public void runInteractive() throws OllamaException {
Scanner sc = new Scanner(System.in);
while (true) {
System.out.print("\n[You]: ");
String input = sc.nextLine();
if ("exit".equalsIgnoreCase(input)) break;
this.interact(
input,
new OllamaChatStreamObserver(
new ConsoleOutputGenerateTokenHandler(),
new ConsoleOutputGenerateTokenHandler()));
}
}
/**
* Bean describing an agent as definable from YAML.
*
* <ul>
* <li>{@code name}: Agent display name
* <li>{@code description}: Freeform description
* <li>{@code tools}: List of tools/functions to enable
* <li>{@code host}: Target Ollama host address
* <li>{@code model}: Name of Ollama model to use
* <li>{@code customPrompt}: Agent's custom base prompt
* <li>{@code requestTimeoutSeconds}: Timeout for requests
* </ul>
*/
@Data
public static class AgentSpec {
private String name;
private String description;
private List<AgentToolSpec> tools;
private String host;
private String model;
private String customPrompt;
private int requestTimeoutSeconds;
}
/**
* Subclass extension of {@link Tools.ToolSpec}, which allows associating a tool with a function
* implementation (via FQCN).
*/
@Data
@Setter
@Getter
@EqualsAndHashCode(callSuper = false)
private static class AgentToolSpec extends Tools.ToolSpec {
/** Fully qualified class name of the tool's {@link ToolFunction} implementation */
private String toolFunctionFQCN = null;
/** Instance of the {@link ToolFunction} to invoke */
private ToolFunction toolFunctionInstance = null;
}
/** Bean for describing a tool function parameter for use in agent YAML definitions. */
@Data
public class AgentToolParameter {
/** The parameter's type (e.g., string, number, etc.) */
private String type;
/** Description of the parameter */
private String description;
/** Whether this parameter is required */
private boolean required;
/**
* Enum values (if any) that this parameter may take; _enum used because 'enum' is reserved
*/
private List<String> _enum; // `enum` is a reserved keyword, so use _enum or similar
}
}

View File

@@ -141,7 +141,6 @@ public class OllamaChatEndpointCaller extends OllamaEndpointCaller {
responseBuffer);
if (statusCode != 200) {
LOG.error("Status code: {}", statusCode);
System.out.println(responseBuffer);
throw new OllamaException(responseBuffer.toString());
}
if (wantedToolsForStream != null && ollamaChatResponseModel != null) {

View File

@@ -136,18 +136,21 @@ public class OllamaGenerateEndpointCaller extends OllamaEndpointCaller {
thinkingBuffer.toString(),
endTime - startTime,
statusCode);
ollamaResult.setModel(ollamaGenerateResponseModel.getModel());
ollamaResult.setCreatedAt(ollamaGenerateResponseModel.getCreatedAt());
ollamaResult.setDone(ollamaGenerateResponseModel.isDone());
ollamaResult.setDoneReason(ollamaGenerateResponseModel.getDoneReason());
ollamaResult.setContext(ollamaGenerateResponseModel.getContext());
ollamaResult.setTotalDuration(ollamaGenerateResponseModel.getTotalDuration());
ollamaResult.setLoadDuration(ollamaGenerateResponseModel.getLoadDuration());
ollamaResult.setPromptEvalCount(ollamaGenerateResponseModel.getPromptEvalCount());
ollamaResult.setPromptEvalDuration(ollamaGenerateResponseModel.getPromptEvalDuration());
ollamaResult.setEvalCount(ollamaGenerateResponseModel.getEvalCount());
ollamaResult.setEvalDuration(ollamaGenerateResponseModel.getEvalDuration());
if (ollamaGenerateResponseModel != null) {
ollamaResult.setModel(ollamaGenerateResponseModel.getModel());
ollamaResult.setCreatedAt(ollamaGenerateResponseModel.getCreatedAt());
ollamaResult.setDone(ollamaGenerateResponseModel.isDone());
ollamaResult.setDoneReason(ollamaGenerateResponseModel.getDoneReason());
ollamaResult.setContext(ollamaGenerateResponseModel.getContext());
ollamaResult.setTotalDuration(ollamaGenerateResponseModel.getTotalDuration());
ollamaResult.setLoadDuration(ollamaGenerateResponseModel.getLoadDuration());
ollamaResult.setPromptEvalCount(ollamaGenerateResponseModel.getPromptEvalCount());
ollamaResult.setPromptEvalDuration(
ollamaGenerateResponseModel.getPromptEvalDuration());
ollamaResult.setEvalCount(ollamaGenerateResponseModel.getEvalCount());
ollamaResult.setEvalDuration(ollamaGenerateResponseModel.getEvalDuration());
}
LOG.debug("Model plain response: {}", ollamaGenerateResponseModel);
LOG.debug("Model response: {}", ollamaResult);
return ollamaResult;
}

View File

@@ -11,7 +11,11 @@ package io.github.ollama4j.tools;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -116,4 +120,53 @@ public class Tools {
@JsonIgnore private boolean required;
}
public static List<Tool> fromJSONFile(String filePath, Map<String, ToolFunction> functionMap) {
try {
ObjectMapper mapper = new ObjectMapper();
List<Map<String, Object>> rawTools =
mapper.readValue(
new File(filePath),
new com.fasterxml.jackson.core.type.TypeReference<>() {});
List<Tool> tools = new ArrayList<>();
for (Map<String, Object> rawTool : rawTools) {
String json = mapper.writeValueAsString(rawTool);
Tool tool = mapper.readValue(json, Tool.class);
String toolName = tool.getToolSpec().getName();
for (Map.Entry<String, ToolFunction> toolFunctionEntry : functionMap.entrySet()) {
if (toolFunctionEntry.getKey().equals(toolName)) {
tool.setToolFunction(toolFunctionEntry.getValue());
}
}
tools.add(tool);
}
return tools;
} catch (Exception e) {
throw new RuntimeException("Failed to load tools from file: " + filePath, e);
}
}
public static List<Tool> fromYAMLFile(String filePath, Map<String, ToolFunction> functionMap) {
try {
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
List<Map<String, Object>> rawTools =
mapper.readValue(new File(filePath), new TypeReference<>() {});
List<Tool> tools = new ArrayList<>();
for (Map<String, Object> rawTool : rawTools) {
String yaml = mapper.writeValueAsString(rawTool);
Tool tool = mapper.readValue(yaml, Tool.class);
String toolName = tool.getToolSpec().getName();
ToolFunction function = functionMap.get(toolName);
if (function != null) {
tool.setToolFunction(function);
}
tools.add(tool);
}
return tools;
} catch (Exception e) {
throw new RuntimeException("Failed to load tools from YAML file: " + filePath, e);
}
}
}