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.

This commit is contained in:
amithkoujalgi 2025-10-10 23:56:31 +05:30
parent 64c629775a
commit da6d20d118
No known key found for this signature in database
GPG Key ID: E29A37746AF94B70
6 changed files with 330 additions and 15 deletions

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"; \

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);
@ -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,93 @@
/*
* 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 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.Tools;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Agent {
private final String name;
private final List<Tools.Tool> tools;
private final Ollama ollamaClient;
private final String model;
private final List<OllamaChatMessage> chatHistory;
public Agent(String name, Ollama ollamaClient, String model, List<Tools.Tool> tools) {
this.name = name;
this.ollamaClient = ollamaClient;
this.chatHistory = new ArrayList<>();
this.tools = tools;
this.model = model;
}
public String think(String userInput) throws OllamaException {
StringBuilder availableToolsDescription = new StringBuilder();
if (!tools.isEmpty()) {
for (Tools.Tool t : tools) {
String toolName = t.getToolSpec().getName();
String toolDescription = t.getToolSpec().getDescription();
availableToolsDescription.append(
"\nTool name: '"
+ toolName
+ "'. Tool Description: '"
+ toolDescription
+ "'.\n");
}
}
if (chatHistory.isEmpty()) {
chatHistory.add(
new OllamaChatMessage(
OllamaChatMessageRole.SYSTEM,
"You are a helpful assistant named "
+ name
+ ". You only perform tasks using tools available for you. You"
+ " respond very precisely and you don't overthink or be too"
+ " creative. Do not ever reveal the tool specification in"
+ " terms of code or JSON or in a way that a software engineer"
+ " sees it. Just be careful with your responses and respond"
+ " like a human. Note that you only execute tools provided to"
+ " you. Following are the tools that you have access to and"
+ " you can perform right actions using right tools."
+ availableToolsDescription));
}
OllamaChatRequest request =
OllamaChatRequest.builder()
.withTools(tools)
.withUseTools(true)
.withModel(model)
.withMessages(chatHistory)
.withMessage(OllamaChatMessageRole.USER, userInput)
.build();
request.withMessage(OllamaChatMessageRole.USER, userInput);
OllamaChatStreamObserver chatTokenHandler =
new OllamaChatStreamObserver(
new ConsoleOutputGenerateTokenHandler(),
new ConsoleOutputGenerateTokenHandler());
OllamaChatResult response = ollamaClient.chat(request, chatTokenHandler);
chatHistory.clear();
chatHistory.addAll(response.getChatHistory());
return response.getResponseModel().getMessage().getResponse();
}
public void runInteractive() throws OllamaException {
Scanner sc = new Scanner(System.in);
while (true) {
System.out.print("\nYou: ");
String input = sc.nextLine();
if ("exit".equalsIgnoreCase(input)) break;
String response = this.think(input);
}
}
}

View File

@ -0,0 +1,215 @@
/*
* 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 io.github.ollama4j.Ollama;
import io.github.ollama4j.exceptions.OllamaException;
import io.github.ollama4j.tools.ToolFunction;
import io.github.ollama4j.tools.Tools;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class SampleAgent {
public static void main(String[] args) throws OllamaException {
Ollama ollama = new Ollama("http://192.168.29.224:11434");
ollama.setRequestTimeoutSeconds(120);
String model = "mistral:7b";
ollama.pullModel(model);
List<Tools.Tool> tools = new ArrayList<>();
// Weather tool
tools.add(
Tools.Tool.builder()
.toolSpec(
Tools.ToolSpec.builder()
.name("weather-tool")
.description(
"Gets current weather for a given location and a"
+ " given day")
.parameters(
Tools.Parameters.of(
Map.of(
"location",
Tools.Property.builder()
.type("string")
.description(
"The location to"
+ " get the"
+ " weather"
+ " for.")
.required(true)
.build(),
"day",
Tools.Property.builder()
.type("string")
.description(
"The day of the"
+ " week to get"
+ " the weather"
+ " for.")
.required(true)
.build())))
.build())
.toolFunction(new WeatherToolFunction())
.build());
// Calculator tool
tools.add(
Tools.Tool.builder()
.toolSpec(
Tools.ToolSpec.builder()
.name("calculator-tool")
.description(
"Performs a simple arithmetic operation between two"
+ " numbers")
.parameters(
Tools.Parameters.of(
Map.of(
"operation",
Tools.Property.builder()
.type("string")
.description(
"Arithmetic"
+ " operation"
+ " to perform:"
+ " add,"
+ " subtract,"
+ " multiply,"
+ " divide")
.required(true)
.build(),
"a",
Tools.Property.builder()
.type("number")
.description(
"The first operand")
.required(true)
.build(),
"b",
Tools.Property.builder()
.type("number")
.description(
"The second"
+ " operand")
.required(true)
.build())))
.build())
.toolFunction(new CalculatorToolFunction())
.build());
// Hotel Booking tool (dummy)
tools.add(
Tools.Tool.builder()
.toolSpec(
Tools.ToolSpec.builder()
.name("hotel-booking-tool")
.description(
"Helps with booking a hotel room in a specified"
+ " city for given dates and number of guests.")
.parameters(
Tools.Parameters.of(
Map.of(
"city",
Tools.Property.builder()
.type("string")
.description(
"The city where you"
+ " want to"
+ " book the"
+ " hotel.")
.required(true)
.build(),
"checkin_date",
Tools.Property.builder()
.type("string")
.description(
"Check-in date.")
.required(true)
.build(),
"checkout_date",
Tools.Property.builder()
.type("string")
.description(
"Check-out date.")
.required(true)
.build(),
"guests",
Tools.Property.builder()
.type("number")
.description(
"Number of guests.")
.required(true)
.build())))
.build())
.toolFunction(new HotelBookingToolFunction())
.build());
Agent agent = new Agent("Nimma Mirta", ollama, model, tools);
agent.runInteractive();
}
}
/** ToolFunction implementation for diff checking. */
class WeatherToolFunction implements ToolFunction {
@Override
public Object apply(Map<String, Object> arguments) {
String response =
"Monday: pleasant, Tuesday: Sunny, Wednesday: Windy, Thursday: Cloudy, Friday:"
+ " Rainy, Saturday: Heavy rains, Sunday: Clear";
return response;
}
}
/** ToolFunction implementation for simple calculations. */
class CalculatorToolFunction implements ToolFunction {
@Override
public Object apply(Map<String, Object> arguments) {
String operation = (String) arguments.get("operation");
double a = ((Number) arguments.get("a")).doubleValue();
double b = ((Number) arguments.get("b")).doubleValue();
double result;
switch (operation.toLowerCase()) {
case "add":
result = a + b;
break;
case "subtract":
result = a - b;
break;
case "multiply":
result = a * b;
break;
case "divide":
if (b == 0) {
return "Cannot divide by zero.";
}
result = a / b;
break;
default:
return "Unknown operation: " + operation;
}
return "Result: " + result;
}
}
/** ToolFunction implementation for a dummy hotel booking agent. */
class HotelBookingToolFunction implements ToolFunction {
@Override
public Object apply(Map<String, Object> arguments) {
String city = (String) arguments.get("city");
String checkin = (String) arguments.get("checkin_date");
String checkout = (String) arguments.get("checkout_date");
int guests = ((Number) arguments.get("guests")).intValue();
// Dummy booking confirmation logic
return String.format(
"Booking confirmed! %d guest(s) in %s from %s to %s. (Confirmation #DUMMY1234)",
guests, city, checkin, checkout);
}
}

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