diff --git a/Core/build.gradle b/Core/build.gradle index 1cdce5e..cf8d874 100644 --- a/Core/build.gradle +++ b/Core/build.gradle @@ -3,7 +3,7 @@ plugins { } group = 'me.zacharias' -version = '1.0-SNAPSHOT' +version = '1.3' dependencies { diff --git a/Core/src/main/java/me/zacharias/chat/core/Core.java b/Core/src/main/java/me/zacharias/chat/core/Core.java index 6b1c102..60f4d1d 100644 --- a/Core/src/main/java/me/zacharias/chat/core/Core.java +++ b/Core/src/main/java/me/zacharias/chat/core/Core.java @@ -23,6 +23,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import static me.zacharias.chat.ollama.OllamaFunctionArgument.deconstructOllamaFunctionArguments; + /** * The Main class for the System, responsible for managing the OllamaObject, tools, and the Ollama API. */ @@ -52,7 +54,7 @@ public class Core { /** * The IP of the Ollama API. */ - private String ollamaIP = "192.168.5.178";//"192.168.5.184"; + private String ollamaIP = "10.0.1.101";//"192.168.5.184"; /** * The port of the Ollama API. */ @@ -71,11 +73,15 @@ public class Core { */ private boolean supportColor; - public static final String DATA; - public static final File DATA_DIR; - public static final File PLUGIN_DIRECTORY; + public static String DATA; + public static File DATA_DIR; + public static File PLUGIN_DIRECTORY; static { + setDataDirectory("AI-Chat"); + } + + public static void setDataDirectory(String dataDirectory) { String data; if(System.getenv("AI_CHAT_DEBUG") != null) { @@ -86,13 +92,13 @@ public class Core { if(localappdata == null) { localappdata = System.getenv("APPDATA"); } - data = localappdata + "/AI-CHAT"; + data = localappdata + "/"+ dataDirectory; } else if (System.getProperty("os.name").toLowerCase().contains("linux")) { - data = System.getenv("HOME") + "/.local/share/AI-CHAT"; + data = System.getenv("HOME") + "/.local/share/" + dataDirectory; } else if (System.getProperty("os.name").toLowerCase().contains("mac")) { - data = System.getProperty("user.home") + "/Library/Application Support/AI-CHAT"; + data = System.getProperty("user.home") + "/Library/Application Support/"+ dataDirectory; } else { data = "./data"; @@ -193,7 +199,7 @@ public class Core { messagesWriter.write(messages.toString()); messagesWriter.close(); - File f = new File("./data/messages.json"); + File f = new File(DATA_DIR,"messages.json"); if(f.exists()) { f.delete(); @@ -261,7 +267,7 @@ public class Core { */ public void addTool(OllamaFunctionTool functionTool, @MagicConstant(valuesFromClass = Source.class) String source) { funtionTools.add(new Pair<>(functionTool, source)); - ollamaObject.addTool(functionTool); + ollamaObject.addTool(functionTool, source); } /** @@ -404,32 +410,34 @@ public class Core { JSONObject function = jsonObject.getJSONObject("function"); List> functions = funtionTools.stream().filter(func -> (func.getKey().name()+func.getValue()).equalsIgnoreCase(function.getString("name"))).toList(); + ArrayList argumentArrayList = new ArrayList<>(); + + JSONObject arguments = function.getJSONObject("arguments"); + + for (String key : arguments.keySet()) { + argumentArrayList.add(new OllamaFunctionArgument(key, arguments.get(key))); + } + if(functions.isEmpty()) { ollamaObject.addMessage(new OllamaToolError("Function '"+function.getString("name")+"' does not exist")); - printMessageHandler.printMessage((supportColor ?"\u001b[31m":"")+"Tried funtion call "+function.getString("name")+" but failed to find it."+(printMessageHandler.color()?"\u001b[0m":"")); + printMessageHandler.printMessage((supportColor ?"\u001b[31m":"")+"Tried funtion call "+function.getString("name")+"("+deconstructOllamaFunctionArguments(argumentArrayList)+") but failed to find it."+(printMessageHandler.color()?"\u001b[0m":"")); writeLog("Failed function call to "+function.getString("name")); } else { OllamaFunctionTool func = functions.getFirst().getKey(); - ArrayList argumentArrayList = new ArrayList<>(); - - JSONObject arguments = function.getJSONObject("arguments"); - - for (String key : arguments.keySet()) { - argumentArrayList.add(new OllamaFunctionArgument(key, arguments.get(key))); - } + // TODO: Check so all arguments is the correct type, else error out in a safe mannet. try { OllamaToolRespnce function1 = func.function(argumentArrayList.toArray(new OllamaFunctionArgument[0])); ollamaObject.addMessage(function1); - printMessageHandler.printMessage((supportColor?"\u001b[34m":"")+"Call "+func.name() + (supportColor?"\u001b[0m":"")); + printMessageHandler.printMessage((supportColor?"\u001b[34m":"")+"Call "+func.name() + "(" + deconstructOllamaFunctionArguments(argumentArrayList) + ")" + (supportColor?"\u001b[0m":"")); writeLog("Successfully function call " + func.name() + " output: " + function1.getResponse()); } catch (OllamaToolErrorException e) { ollamaObject.addMessage(new OllamaToolError(e.getMessage())); - printMessageHandler.printMessage((supportColor?"\u001b[31m":"")+"Tried funtion call " + func.name() + " but failed due to " + e.getError() + (supportColor?"\u001b[0m":"")); - writeLog(e.getMessage()); + printMessageHandler.printMessage((supportColor?"\u001b[31m":"")+"Tried funtion call " + func.name() + "("+deconstructOllamaFunctionArguments(argumentArrayList)+") but failed due to " + e.getError() + (supportColor?"\u001b[0m":"")); + writeLog("ERROR: "+e.getMessage()); } } } diff --git a/Core/src/main/java/me/zacharias/chat/core/LaunchOptions.java b/Core/src/main/java/me/zacharias/chat/core/LaunchOptions.java index 61d59ed..02dc161 100644 --- a/Core/src/main/java/me/zacharias/chat/core/LaunchOptions.java +++ b/Core/src/main/java/me/zacharias/chat/core/LaunchOptions.java @@ -15,6 +15,8 @@ public class LaunchOptions { return instance; } + private LaunchOptions() {} + private boolean loadOld = true; private boolean autoAccept; private boolean serverMode; diff --git a/Core/src/main/java/me/zacharias/chat/core/PrintMessageHandler.java b/Core/src/main/java/me/zacharias/chat/core/PrintMessageHandler.java index ebedc19..fd189fd 100644 --- a/Core/src/main/java/me/zacharias/chat/core/PrintMessageHandler.java +++ b/Core/src/main/java/me/zacharias/chat/core/PrintMessageHandler.java @@ -18,4 +18,18 @@ public interface PrintMessageHandler { * @return a boolean indicating if color is supported by the PrintMessageHandler. */ boolean color(); + + /** + * Default method to print error messages to the user or API Client. + * This uses ANSI escape codes to color the output red if color is supported. + * @param message The error message to be printed. + * If color is not supported, it will print the message without color. + */ + default void printError(String message) { + if (color()) { + printMessage("\u001B[31m" + message + "\u001B[0m"); // Red color for errors + } else { + printMessage(message); + } + } } diff --git a/Core/src/main/java/me/zacharias/chat/ollama/OllamaFunctionArgument.java b/Core/src/main/java/me/zacharias/chat/ollama/OllamaFunctionArgument.java index add4a05..15cd8cc 100644 --- a/Core/src/main/java/me/zacharias/chat/ollama/OllamaFunctionArgument.java +++ b/Core/src/main/java/me/zacharias/chat/ollama/OllamaFunctionArgument.java @@ -1,5 +1,7 @@ package me.zacharias.chat.ollama; +import java.util.ArrayList; + /** * Represents an argument passed to a tool. * @@ -37,4 +39,24 @@ public record OllamaFunctionArgument(String argument, Object value) { public Object value() { return value; } + + public static String deconstructOllamaFunctionArgument(OllamaFunctionArgument argument) { + return argument.argument() + ": " + argument.value(); + } + + public static String deconstructOllamaFunctionArguments(OllamaFunctionArgument... arguments) { + StringBuilder sb = new StringBuilder(); + for (OllamaFunctionArgument argument : arguments) { + sb.append(deconstructOllamaFunctionArgument(argument)).append(", "); + } + return sb.toString().replaceAll(", $", ""); + } + + public static String deconstructOllamaFunctionArguments(ArrayList arguments) { + StringBuilder sb = new StringBuilder(); + for (OllamaFunctionArgument argument : arguments) { + sb.append(deconstructOllamaFunctionArgument(argument)).append(", "); + } + return sb.toString().replaceAll(", $", ""); + } } diff --git a/Core/src/main/java/me/zacharias/chat/ollama/OllamaFunctionTool.java b/Core/src/main/java/me/zacharias/chat/ollama/OllamaFunctionTool.java index 2d781dd..6cd7c0f 100644 --- a/Core/src/main/java/me/zacharias/chat/ollama/OllamaFunctionTool.java +++ b/Core/src/main/java/me/zacharias/chat/ollama/OllamaFunctionTool.java @@ -9,13 +9,15 @@ import org.json.JSONObject; */ public abstract class OllamaFunctionTool implements OllamaTool { + protected String source = ""; + @Override public String toString() { JSONObject ret = new JSONObject(); ret.put("tool", "function"); JSONObject function = new JSONObject(); - function.put("name", name()); + function.put("name", name()+(source != null ? source : "")); function.put("description", description()); function.put("parameters", (parameters() == null? new JSONObject() : new JSONObject(parameters().toString()))); diff --git a/Core/src/main/java/me/zacharias/chat/ollama/OllamaObject.java b/Core/src/main/java/me/zacharias/chat/ollama/OllamaObject.java index 8cf3a7f..6b99b2b 100644 --- a/Core/src/main/java/me/zacharias/chat/ollama/OllamaObject.java +++ b/Core/src/main/java/me/zacharias/chat/ollama/OllamaObject.java @@ -3,6 +3,7 @@ package me.zacharias.chat.ollama; import com.sun.source.util.Plugin; import me.zacharias.chat.core.Core; import me.zacharias.chat.core.LaunchOptions; +import me.zacharias.chat.core.Pair; import me.zacharias.chat.core.files.FileHandlerLocation; import me.zacharias.chat.core.files.FileHandler; @@ -13,6 +14,7 @@ import org.json.JSONObject; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; +import java.lang.reflect.Field; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; @@ -41,7 +43,7 @@ public class OllamaObject { /** * The tools of the Ollama Object. */ - ArrayList tools; + ArrayList> tools; /** * The format of the Ollama Object. */ @@ -69,7 +71,7 @@ public class OllamaObject { * @param stream If the Ollama Object is streamed. see {@link OllamaObject#stream} * @param keep_alive The keep alive of the Ollama Object. see {@link OllamaObject#keep_alive} */ - private OllamaObject(String model, ArrayList messages, ArrayList tools, JSONObject format, Map options, boolean stream, String keep_alive) { + private OllamaObject(String model, ArrayList messages, ArrayList> tools, JSONObject format, Map options, boolean stream, String keep_alive) { this.model = model; this.messages = messages; this.tools = tools; @@ -77,7 +79,7 @@ public class OllamaObject { this.options = options; this.stream = stream; this.keep_alive = keep_alive; - LaunchOptions launchOptions = new LaunchOptions(); + LaunchOptions launchOptions = LaunchOptions.getInstance(); if(launchOptions.isLoadOld()) { System.out.println("Loading old data..."); File f = new File(Core.DATA_DIR+"/messages.json"); @@ -105,6 +107,10 @@ public class OllamaObject { e.printStackTrace(); } } + else + { + System.out.println("No old data found, skipping loading old data."); + } } } @@ -128,7 +134,7 @@ public class OllamaObject { * Gets the tools * @return The tools */ - public ArrayList getTools() { + public ArrayList> getTools() { return tools; } @@ -136,8 +142,29 @@ public class OllamaObject { * Adds a tool to the Ollama Object * @param tool The tool to add */ - public void addTool(OllamaTool tool) { - tools.add(tool); + public void addTool(OllamaTool tool, @MagicConstant(valuesFromClass = Core.Source.class) String source) { + // We inject the source into the tool's source field if it exists, This is to not cause issues with duplicate tools + Class clazz = tool.getClass(); + Field field = null; + while(!clazz.equals(Object.class)) { + try { + field = clazz.getDeclaredField("source"); + if (field != null) { + break; + } + } + catch (NoSuchFieldException ignore){} + clazz = clazz.getSuperclass(); + } + if (field != null) { + field.setAccessible(true); + try { + field.set(tool, source); + } catch (IllegalAccessException e) { + Core.writeLog("ERROR: "+e.getMessage()); + } + } + tools.add(new Pair<>(tool, source)); } public void removeTool(OllamaTool tool) { @@ -189,8 +216,11 @@ public class OllamaObject { JSONObject json = new JSONObject(); JSONArray tools = new JSONArray(); - for (OllamaTool tool : this.tools) { - tools.put(new JSONObject(tool.toString())); + for (Pair tool : this.tools) { + if(tool.getKey().getClass().isInterface()) continue; + JSONObject obj = new JSONObject(tool.getKey().toString()); + //obj.put("name", obj.getString("name") + tool.getValue()); // Injects the source of the tool into the name + tools.put(obj); } JSONArray messages = new JSONArray(); @@ -207,7 +237,7 @@ public class OllamaObject { json.put("keep_alive", keep_alive); return json.toString(); } - + /** * Creates a new instance of OllamaObjectBuilder. * @return The {@link OllamaObjectBuilder} @@ -232,7 +262,7 @@ public class OllamaObject { /** * The tools of the Ollama Object. */ - ArrayList tools = new ArrayList<>(); + ArrayList> tools = new ArrayList<>(); /** * The format of the Ollama Object. */ @@ -321,30 +351,72 @@ public class OllamaObject { /** * Adds a tool to the Ollama Object + * Assumes the tool is from an external source, see {@link OllamaObject.OllamaObjectBuilder#addTool(OllamaTool, String)} to specify the source * @param tool The tool to add * @return The {@link OllamaObjectBuilder} */ public OllamaObjectBuilder addTool(OllamaTool tool) { - this.tools.add(tool); + this.tools.add(new Pair<>(tool, Core.Source.EXTERNAL)); + return this; + } + + /** + * Adds a tool to the Ollama Object + * This allows you to specify the source of the tool, see {@link OllamaObject.OllamaObjectBuilder#addTool(OllamaTool)} to add a tool from an external source + * @param tool The tool to add + * @return The {@link OllamaObjectBuilder} + */ + public OllamaObjectBuilder addTool(OllamaTool tool, @MagicConstant(valuesFromClass = Core.Source.class) String source) { + this.tools.add(new Pair<>(tool, source)); return this; } /** * Adds tools to the Ollama Object + * Assumes the tools are from an external source, see {@link OllamaObject.OllamaObjectBuilder#addTools(ArrayList)}} to specify the source * @param tools The tools to add * @return The {@link OllamaObjectBuilder} */ - public OllamaObjectBuilder addTools(ArrayList tools) { - this.tools.addAll(tools); + public OllamaObjectBuilder addToolsExternal(ArrayList tools) { + for (OllamaTool tool : tools) { + this.tools.add(new Pair<>(tool, Core.Source.EXTERNAL)); + } + return this; + } + + /** + * Adds tools to the Ollama Object + * This allows you to specify the source of the tools, see {@link OllamaObject.OllamaObjectBuilder#addToolsExternal(ArrayList)}} to add tools from an external source + * @param tools The tools to add + * @return The {@link OllamaObjectBuilder} + */ + public OllamaObjectBuilder addTools(ArrayList> tools) { + for(Pair tool : tools) { + this.tools.add(new Pair<>(tool.getKey(), tool.getValue())); + } return this; } /** * Adds tools to the Ollama Object + * Assumes the tools are from an external source, see {@link OllamaObject.OllamaObjectBuilder#addTools(Pair[])}} to specify the source * @param tools The tools to add * @return The {@link OllamaObjectBuilder} */ public OllamaObjectBuilder addTools(OllamaTool... tools) { + for(OllamaTool tool : tools) { + this.tools.add(new Pair<>(tool, Core.Source.EXTERNAL)); + } + return this; + } + + /** + * Adds tools to the Ollama Object + * This allows you to specify the source of the tools, see {@link OllamaObject.OllamaObjectBuilder#addTools(OllamaTool[])} to add tools from an external source + * @param tools The tools to add + * @return The {@link OllamaObjectBuilder} + */ + public OllamaObjectBuilder addTools(Pair... tools) { this.tools.addAll(List.of(tools)); return this; } diff --git a/Core/src/main/java/me/zacharias/chat/ollama/OllamaPerameter.java b/Core/src/main/java/me/zacharias/chat/ollama/OllamaPerameter.java index 1f798ed..3049920 100644 --- a/Core/src/main/java/me/zacharias/chat/ollama/OllamaPerameter.java +++ b/Core/src/main/java/me/zacharias/chat/ollama/OllamaPerameter.java @@ -200,7 +200,11 @@ public class OllamaPerameter { /** * Represents a array parameter. */ - ARRAY("array"); + ARRAY("array"), + /** + * Represents a object parameter. + */ + OBJECT("object"); /** * The type of the parameter. diff --git a/Display/src/main/java/me/zacharias/chat/display/Display.java b/Display/src/main/java/me/zacharias/chat/display/Display.java index a266614..638d8f5 100644 --- a/Display/src/main/java/me/zacharias/chat/display/Display.java +++ b/Display/src/main/java/me/zacharias/chat/display/Display.java @@ -109,6 +109,7 @@ public class Display { /bye Exits the program. /write Flushes the current log stream to file. /list Lists all available tools. + /corelist Lists all tools according to the OllamaObject. /working Prints the current working directories. /peek Peeks the current memory. """); @@ -153,6 +154,14 @@ public class Display { writeLog("Function: " + funtion.getKey().name() + "(" + args + ") [" + funtion.getValue() + "]"); } break; + case "corelist": + writeLog("Tools installed in this instance acording to the coire OllamaObject"); + + for(Pair funtion : core.getOllamaObject().getTools()) { + System.out.println("> Function: " + funtion.getKey().toString()); + writeLog("Function: " + funtion.getKey().toString()); + } + break; case "working": System.out.println("Working directories:\n" + " Data: " + Core.DATA_DIR.getAbsolutePath() + "\n" + diff --git a/GeniusAPI/src/main/java/me/zacharias/neuro/dock/genius/endpoints/GetLyrics.java b/GeniusAPI/src/main/java/me/zacharias/neuro/dock/genius/endpoints/GetLyrics.java index 2096bcb..5a4818e 100644 --- a/GeniusAPI/src/main/java/me/zacharias/neuro/dock/genius/endpoints/GetLyrics.java +++ b/GeniusAPI/src/main/java/me/zacharias/neuro/dock/genius/endpoints/GetLyrics.java @@ -59,6 +59,10 @@ public class GetLyrics extends GeniusEndpointTool { @Override public OllamaToolRespnce function(OllamaFunctionArgument... args) { + if(!( args[0].value() instanceof Integer)) { + throw new OllamaToolErrorException(this.name(), "The song_id must be an integer."); + } + String lyricsStr = geniusToolsInstance.hasCache((int) args[0].value()); if(lyricsStr != null) diff --git a/build.gradle b/build.gradle index 37ee291..0701c94 100644 --- a/build.gradle +++ b/build.gradle @@ -7,6 +7,9 @@ group = 'me.zacharias' version = '1.0-SNAPSHOT' allprojects { + + apply plugin: 'maven-publish' + repositories { mavenCentral() } @@ -43,4 +46,32 @@ subprojects { archives sourcesJar archives javadocJar } + + // Make Javadoc generation non-fatal to avoid publish failures on bad Javadoc + tasks.withType(Javadoc).configureEach { + options.addBooleanOption('Xdoclint:none', true) + failOnError = false + } + + // Configure local Maven publishing for modules that have a Java component + afterEvaluate { proj -> + if (components.findByName("java") != null) { + publishing { + publications { + mavenJava(MavenPublication) { + from components.java + if (tasks.findByName('sourcesJar')) artifact tasks.sourcesJar + if (tasks.findByName('javadocJar')) artifact tasks.javadocJar + pom { + name = proj.name + description = "${proj.name} module of AI-test" + } + } + } + repositories { + mavenLocal() + } + } + } + } } \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 7e4bf65..78a8342 100644 --- a/settings.gradle +++ b/settings.gradle @@ -11,4 +11,5 @@ include 'MALAPITool' * The core NeuroDock system will still function without this module. */ include 'GeniusAPI' + include 'WikipediaTool' \ No newline at end of file