Ollama4j
A Java library (wrapper/binding) for Ollama server.
Loading...
Searching...
No Matches
Agent.java
Go to the documentation of this file.
1/*
2 * Ollama4j - Java library for interacting with Ollama server.
3 * Copyright (c) 2025 Amith Koujalgi and contributors.
4 *
5 * Licensed under the MIT License (the "License");
6 * you may not use this file except in compliance with the License.
7 *
8*/
9package io.github.ollama4j.agent;
10
11import com.fasterxml.jackson.databind.ObjectMapper;
12import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
13import io.github.ollama4j.Ollama;
14import io.github.ollama4j.exceptions.OllamaException;
15import io.github.ollama4j.impl.ConsoleOutputGenerateTokenHandler;
16import io.github.ollama4j.models.chat.*;
17import io.github.ollama4j.tools.ToolFunction;
18import io.github.ollama4j.tools.Tools;
19import java.io.InputStream;
20import java.util.ArrayList;
21import java.util.List;
22import java.util.Scanner;
23import lombok.*;
24
40public class Agent {
42 private final String name;
43
45 private final List<Tools.Tool> tools;
46
48 private final Ollama ollamaClient;
49
51 private final String model;
52
54 private final List<OllamaChatMessage> chatHistory;
55
57 private final String customPrompt;
58
68 public Agent(
69 String name,
70 Ollama ollamaClient,
71 String model,
72 String customPrompt,
73 List<Tools.Tool> tools) {
74 this.name = name;
75 this.ollamaClient = ollamaClient;
76 this.chatHistory = new ArrayList<>();
77 this.tools = tools;
78 this.model = model;
79 this.customPrompt = customPrompt;
80 }
81
92 public static Agent load(String yamlPathOrResource) {
93 try {
94 ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
95
96 InputStream input =
97 Agent.class.getClassLoader().getResourceAsStream(yamlPathOrResource);
98 if (input == null) {
99 java.nio.file.Path filePath = java.nio.file.Paths.get(yamlPathOrResource);
100 if (java.nio.file.Files.exists(filePath)) {
101 input = java.nio.file.Files.newInputStream(filePath);
102 } else {
103 throw new RuntimeException(
104 yamlPathOrResource + " not found in classpath or file system");
105 }
106 }
107 AgentSpec agentSpec = mapper.readValue(input, AgentSpec.class);
108 List<AgentToolSpec> tools = agentSpec.getTools();
109 for (AgentToolSpec tool : tools) {
110 String fqcn = tool.getToolFunctionFQCN();
111 if (fqcn != null && !fqcn.isEmpty()) {
112 try {
113 Class<?> clazz = Class.forName(fqcn);
114 Object instance = clazz.getDeclaredConstructor().newInstance();
115 if (instance instanceof ToolFunction) {
116 tool.setToolFunctionInstance((ToolFunction) instance);
117 } else {
118 throw new RuntimeException(
119 "Class does not implement ToolFunction: " + fqcn);
120 }
121 } catch (Exception e) {
122 throw new RuntimeException(
123 "Failed to instantiate tool function: " + fqcn, e);
124 }
125 }
126 }
127 List<Tools.Tool> agentTools = new ArrayList<>();
128 for (AgentToolSpec a : tools) {
129 Tools.Tool t = new Tools.Tool();
130 t.setToolFunction(a.getToolFunctionInstance());
131 Tools.ToolSpec ts = new Tools.ToolSpec();
132 ts.setName(a.getName());
133 ts.setDescription(a.getDescription());
134 ts.setParameters(a.getParameters());
135 t.setToolSpec(ts);
136 agentTools.add(t);
137 }
138 Ollama ollama = new Ollama(agentSpec.getHost());
139 ollama.setRequestTimeoutSeconds(120);
140 ollama.pullModel(agentSpec.getModel());
141 return new Agent(
142 agentSpec.getName(),
143 ollama,
144 agentSpec.getModel(),
145 agentSpec.getCustomPrompt(),
146 agentTools);
147 } catch (Exception e) {
148 throw new RuntimeException("Failed to load agent from YAML", e);
149 }
150 }
151
162 public List<OllamaChatMessage> interact(
163 String userInput, OllamaChatStreamObserver chatTokenHandler) throws OllamaException {
164 // Build a concise and readable description of available tools
165 String availableToolsDescription =
166 tools.isEmpty()
167 ? ""
168 : tools.stream()
169 .map(
170 t ->
171 String.format(
172 "- %s: %s",
173 t.getToolSpec().getName(),
174 t.getToolSpec().getDescription() != null
175 ? t.getToolSpec().getDescription()
176 : "No description"))
177 .reduce((a, b) -> a + "\n" + b)
178 .map(desc -> "\nYou have access to the following tools:\n" + desc)
179 .orElse("");
180
181 // Add system prompt if chatHistory is empty
182 if (chatHistory.isEmpty()) {
183 String systemPrompt =
184 String.format(
185 "You are a helpful AI assistant named %s. Your actions are limited to"
186 + " using the available tools. %s%s",
187 name,
188 (customPrompt != null ? customPrompt : ""),
189 availableToolsDescription);
190 chatHistory.add(new OllamaChatMessage(OllamaChatMessageRole.SYSTEM, systemPrompt));
191 }
192
193 // Add the user input as a message before sending request
194 chatHistory.add(new OllamaChatMessage(OllamaChatMessageRole.USER, userInput));
195
196 OllamaChatRequest request =
198 .withTools(tools)
199 .withUseTools(true)
200 .withModel(model)
201 .withMessages(chatHistory)
202 .build();
203 OllamaChatResult response = ollamaClient.chat(request, chatTokenHandler);
204
205 chatHistory.clear();
206 chatHistory.addAll(response.getChatHistory());
207
208 return response.getChatHistory();
209 }
210
219 public void runInteractive() throws OllamaException {
220 Scanner sc = new Scanner(System.in);
221 while (true) {
222 System.out.print("\n[You]: ");
223 String input = sc.nextLine();
224 if ("exit".equalsIgnoreCase(input)) break;
225 this.interact(
226 input,
230 }
231 }
232
246 @Data
247 public static class AgentSpec {
248 private String name;
249 private String description;
250 private List<AgentToolSpec> tools;
251 private String host;
252 private String model;
253 private String customPrompt;
254 private int requestTimeoutSeconds;
255 }
256
261 @Data
262 @Setter
263 @Getter
264 @EqualsAndHashCode(callSuper = false)
265 private static class AgentToolSpec extends Tools.ToolSpec {
267 private String toolFunctionFQCN = null;
268
270 private ToolFunction toolFunctionInstance = null;
271 }
272
274 @Data
275 public class AgentToolParameter {
277 private String type;
278
280 private String description;
281
283 private boolean required;
284
288 private List<String> _enum; // `enum` is a reserved keyword, so use _enum or similar
289 }
290}
void pullModel(String modelName)
Definition Ollama.java:461
OllamaChatResult chat(OllamaChatRequest request, OllamaChatTokenHandler tokenHandler)
Definition Ollama.java:875
List< OllamaChatMessage > interact(String userInput, OllamaChatStreamObserver chatTokenHandler)
Definition Agent.java:162
Agent(String name, Ollama ollamaClient, String model, String customPrompt, List< Tools.Tool > tools)
Definition Agent.java:68
static Agent load(String yamlPathOrResource)
Definition Agent.java:92
OllamaChatRequest withTools(List< Tools.Tool > tools)
OllamaChatRequest withUseTools(boolean useTools)
OllamaChatRequest withMessages(List< OllamaChatMessage > messages)