mirror of
https://github.com/amithkoujalgi/ollama4j.git
synced 2025-10-22 21:29:31 +02:00
Remove SampleAgent class and associated YAML configuration file, streamlining the project by eliminating example implementations and their dependencies.
This commit is contained in:
parent
bec634dd37
commit
866c08f590
9
pom.xml
9
pom.xml
@ -260,11 +260,10 @@
|
||||
<version>2.20.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>tools.jackson.dataformat</groupId>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<version>2.20.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
@ -281,7 +280,6 @@
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>2.0.17</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
@ -300,7 +298,6 @@
|
||||
<version>20250517</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>ollama</artifactId>
|
||||
@ -313,14 +310,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>
|
||||
|
@ -8,6 +8,8 @@
|
||||
*/
|
||||
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;
|
||||
@ -19,16 +21,50 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import lombok.*;
|
||||
import tools.jackson.dataformat.yaml.YAMLMapper;
|
||||
|
||||
/**
|
||||
* 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 #fromYaml(String)} for YAML-based configuration.
|
||||
* <li>Handle conversation turns via {@link #think(String)}.
|
||||
* <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,
|
||||
@ -43,17 +79,29 @@ public class Agent {
|
||||
this.customPrompt = customPrompt;
|
||||
}
|
||||
|
||||
public static Agent fromYaml(String agentYaml) {
|
||||
/**
|
||||
* 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 {
|
||||
YAMLMapper mapper = new YAMLMapper();
|
||||
InputStream input = Agent.class.getClassLoader().getResourceAsStream(agentYaml);
|
||||
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(agentYaml);
|
||||
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(
|
||||
agentYaml + " not found in classpath or file system");
|
||||
yamlPathOrResource + " not found in classpath or file system");
|
||||
}
|
||||
}
|
||||
AgentSpec agentSpec = mapper.readValue(input, AgentSpec.class);
|
||||
@ -100,61 +148,105 @@ public class Agent {
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Facilitates a single round of chat for the agent:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Builds/promotes the system prompt on the first turn if necessary
|
||||
* <li>Adds the user's input to chat history
|
||||
* <li>Submits the chat turn to the Ollama model (with tool/function support)
|
||||
* <li>Updates internal chat history in accordance with the Ollama chat result
|
||||
* </ul>
|
||||
*
|
||||
* @param userInput The user's message or question for the agent.
|
||||
* @return The model's response as a string.
|
||||
* @throws OllamaException If there is a problem with the Ollama API.
|
||||
*/
|
||||
public String interact(String userInput) 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()) {
|
||||
chatHistory.add(
|
||||
new OllamaChatMessage(
|
||||
OllamaChatMessageRole.SYSTEM,
|
||||
"You are a helpful assistant named "
|
||||
+ name
|
||||
+ ". You only perform tasks using tools available for you. "
|
||||
+ customPrompt
|
||||
+ ". Following are the tools that you have access to and"
|
||||
+ " you can perform right actions using right tools."
|
||||
+ availableToolsDescription));
|
||||
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)
|
||||
.withMessage(OllamaChatMessageRole.USER, userInput)
|
||||
.build();
|
||||
request.withMessage(OllamaChatMessageRole.USER, userInput);
|
||||
|
||||
OllamaChatStreamObserver chatTokenHandler =
|
||||
new OllamaChatStreamObserver(
|
||||
new ConsoleOutputGenerateTokenHandler(),
|
||||
new ConsoleOutputGenerateTokenHandler());
|
||||
OllamaChatResult response = ollamaClient.chat(request, chatTokenHandler);
|
||||
|
||||
// Update chat history for continuity
|
||||
chatHistory.clear();
|
||||
chatHistory.addAll(response.getChatHistory());
|
||||
|
||||
return response.getResponseModel().getMessage().getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
String response = this.think(input);
|
||||
this.interact(input);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
@ -166,19 +258,36 @@ public class Agent {
|
||||
private int requestTimeoutSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclass extension of {@link Tools.ToolSpec}, which allows associating a tool with a function
|
||||
* implementation (via FQCN).
|
||||
*/
|
||||
@Data
|
||||
@Setter
|
||||
@Getter
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* 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.exceptions.OllamaException;
|
||||
import io.github.ollama4j.tools.ToolFunction;
|
||||
import java.util.Map;
|
||||
|
||||
/** Example usage of the Agent API with some dummy tool functions. */
|
||||
public class SampleAgent {
|
||||
public static void main(String[] args) throws OllamaException {
|
||||
Agent agent = Agent.fromYaml("agent.yaml");
|
||||
agent.runInteractive();
|
||||
}
|
||||
}
|
||||
|
||||
/** ToolFunction implementation that returns a dummy weekly weather forecast. */
|
||||
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 basic arithmetic 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 simulating a hotel booking. */
|
||||
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);
|
||||
}
|
||||
}
|
@ -11,8 +11,10 @@ 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;
|
||||
@ -21,8 +23,6 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.dataformat.yaml.YAMLMapper;
|
||||
|
||||
public class Tools {
|
||||
private Tools() {}
|
||||
@ -150,7 +150,7 @@ public class Tools {
|
||||
|
||||
public static List<Tool> fromYAMLFile(String filePath, Map<String, ToolFunction> functionMap) {
|
||||
try {
|
||||
YAMLMapper mapper = new YAMLMapper();
|
||||
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
|
||||
List<Map<String, Object>> rawTools =
|
||||
mapper.readValue(new File(filePath), new TypeReference<>() {});
|
||||
List<Tool> tools = new ArrayList<>();
|
||||
|
@ -1,46 +0,0 @@
|
||||
name: Nimma Mitra
|
||||
host: http://192.168.29.224:11434
|
||||
model: mistral:7b
|
||||
requestTimeoutSeconds: 120
|
||||
customPrompt: >
|
||||
Only use tools and do not use your creativity.
|
||||
Do not ever tell me to call the tool or how to use the tool or command myself.
|
||||
You do that for me. That is why you exist.
|
||||
You call tool on my behalf and give me a response from the tool.
|
||||
tools:
|
||||
- name: weather-tool
|
||||
toolFunctionFQCN: io.github.ollama4j.agent.WeatherToolFunction
|
||||
description: Gets the current weather for a given location and day.
|
||||
parameters:
|
||||
properties:
|
||||
location:
|
||||
type: string
|
||||
description: The location for which to get the weather.
|
||||
required: true
|
||||
day:
|
||||
type: string
|
||||
description: The day of the week for which to get the weather.
|
||||
required: true
|
||||
|
||||
- name: calculator-tool
|
||||
toolFunctionFQCN: io.github.ollama4j.agent.CalculatorToolFunction
|
||||
description: Performs a simple arithmetic operation between two numbers.
|
||||
parameters:
|
||||
properties:
|
||||
operation:
|
||||
type: string
|
||||
description: The arithmetic operation to perform.
|
||||
enum:
|
||||
- add
|
||||
- subtract
|
||||
- multiply
|
||||
- divide
|
||||
required: true
|
||||
a:
|
||||
type: number
|
||||
description: The first operand.
|
||||
required: true
|
||||
b:
|
||||
type: number
|
||||
description: The second operand.
|
||||
required: true
|
Loading…
x
Reference in New Issue
Block a user