refactor: redesign plugin system and enhance message handling
build / build (push) Has been cancelled
build / build (push) Has been cancelled
- Move plugin and tool-related classes to a dedicated `Plugin-API` module. - Introduce a new plugin lifecycle (`onInit`, `onEnable`, `onDisable`) and a more robust `Tool` API. - Refactor `Core` to use `PrintAdvanceMessageHandler` for improved handling of assistant messages, tool responses, and errors. - Rename `PluginLoader` to `Loader` and migrate it to `me.zacharias.chat.plugin.loader`. - Update build configurations and Java toolchains across modules. - Clean up obsolete plugin annotations and interfaces in the `Core` module.
This commit is contained in:
@@ -8,6 +8,7 @@ build/
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
|
||||
Generated
+1
-1
@@ -1 +1 @@
|
||||
AI-test
|
||||
neurodock
|
||||
Generated
+1
-1
@@ -5,7 +5,6 @@
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleHome" value="" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
@@ -14,6 +13,7 @@
|
||||
<option value="$PROJECT_DIR$/Display" />
|
||||
<option value="$PROJECT_DIR$/GeniusAPI" />
|
||||
<option value="$PROJECT_DIR$/MALAPITool" />
|
||||
<option value="$PROJECT_DIR$/Plugin-API" />
|
||||
<option value="$PROJECT_DIR$/WikipediaTool" />
|
||||
<option value="$PROJECT_DIR$/launcher" />
|
||||
</set>
|
||||
|
||||
Generated
+1
-1
@@ -7,7 +7,7 @@
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="26" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_26" default="true" project-jdk-name="26" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -30,3 +30,9 @@ jar{
|
||||
attributes 'Main-Class': 'me.zacharias.chat.api.APIApplication'
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(javaVersion)) // Set Java version
|
||||
}
|
||||
}
|
||||
+18
-4
@@ -2,15 +2,29 @@ plugins {
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
group = 'me.zacharias'
|
||||
version = '1.3'
|
||||
group = 'me.zacharias.neurodock'
|
||||
version = '1.5'
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation project(":Plugin-API")
|
||||
api "org.json:json:20250107"
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(21)) // Set Java version
|
||||
languageVersion.set(JavaLanguageVersion.of(javaVersion)) // Set Java version
|
||||
}
|
||||
}
|
||||
|
||||
// Tell shadow not to interfere with the main publication
|
||||
shadowJar {
|
||||
archiveClassifier = 'all' // keeps shadow as -all.jar, not the main artifact
|
||||
}
|
||||
|
||||
configurations {
|
||||
[apiElements, runtimeElements].each {
|
||||
it.outgoing.artifacts.removeIf {
|
||||
it.buildDependencies.getDependencies(null).contains(shadowJar)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import me.zacharias.chat.core.memory.*;
|
||||
import me.zacharias.chat.ollama.*;
|
||||
import me.zacharias.chat.ollama.exceptions.OllamaToolErrorException;
|
||||
import me.zacharias.chat.plugin.Plugin;
|
||||
import me.zacharias.chat.plugin.PluginLoader;
|
||||
import me.zacharias.chat.plugin.loader.Loader;
|
||||
import me.zacharias.chat.plugin.exceptions.PluginLoadingException;
|
||||
import org.intellij.lang.annotations.MagicConstant;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -68,11 +68,7 @@ public class Core {
|
||||
/**
|
||||
* The PrintMessageHandler to use.
|
||||
*/
|
||||
private final PrintMessageHandler printMessageHandler;
|
||||
/**
|
||||
* If color is supported by the PrintMessageHandler.
|
||||
*/
|
||||
private boolean supportColor;
|
||||
private final PrintAdvanceMessageHandler printMessageHandler;
|
||||
|
||||
public static String DATA;
|
||||
public static File DATA_DIR;
|
||||
@@ -234,9 +230,8 @@ public class Core {
|
||||
* Creates a new instance of Core with the provided PrintMessageHandler
|
||||
* @param printMessageHandler The PrintMessageHandler to use as the default Output
|
||||
*/
|
||||
public Core(@NotNull PrintMessageHandler printMessageHandler) {
|
||||
public Core(@NotNull PrintAdvanceMessageHandler printMessageHandler) {
|
||||
this.printMessageHandler = printMessageHandler;
|
||||
supportColor = printMessageHandler.color();
|
||||
ollamaIP = "localhost";
|
||||
|
||||
// TODO: This is a dirty way to not have a masisive init block, please check this methods comment.
|
||||
@@ -248,10 +243,9 @@ public class Core {
|
||||
* @param printMessageHandler The PrintMessageHandler to use as the default Output
|
||||
* @param ollamaIP The IP used for the Ollama backend
|
||||
*/
|
||||
public Core(@NotNull PrintMessageHandler printMessageHandler, @NotNull String ollamaIP)
|
||||
public Core(@NotNull PrintAdvanceMessageHandler printMessageHandler, @NotNull String ollamaIP)
|
||||
{
|
||||
this.printMessageHandler = printMessageHandler;
|
||||
supportColor = printMessageHandler.color();
|
||||
this.ollamaIP = ollamaIP;
|
||||
|
||||
// TODO: This is a dirty way to not have a masisive init block, please check this methods comment.
|
||||
@@ -466,8 +460,9 @@ public class Core {
|
||||
}
|
||||
|
||||
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")+"("+deconstructOllamaFunctionArguments(argumentArrayList)+") but failed to find it."+(printMessageHandler.color()?"\u001b[0m":""));
|
||||
OllamaToolError ollamaToolError = new OllamaToolError("Function '" + function.getString("name") + "' does not exist");
|
||||
ollamaObject.addMessage(ollamaToolError);
|
||||
printMessageHandler.printErrorMessage(ollamaToolError);
|
||||
writeLog("Failed function call to "+function.getString("name"));
|
||||
}
|
||||
else {
|
||||
@@ -479,11 +474,12 @@ public class Core {
|
||||
try {
|
||||
OllamaToolRespnce function1 = func.function(argumentArrayList.toArray(new OllamaFunctionArgument[0]));
|
||||
ollamaObject.addMessage(function1);
|
||||
printMessageHandler.printMessage((supportColor?"\u001b[34m":"")+"Call "+func.name() + "(" + deconstructOllamaFunctionArguments(argumentArrayList) + ")" + (supportColor?"\u001b[0m":""));
|
||||
printMessageHandler.printMessage(function1);
|
||||
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() + "("+deconstructOllamaFunctionArguments(argumentArrayList)+") but failed due to " + e.getError() + (supportColor?"\u001b[0m":""));
|
||||
OllamaToolError ollamaToolError = new OllamaToolError(e.getMessage());
|
||||
ollamaObject.addMessage(ollamaToolError);
|
||||
printMessageHandler.printErrorMessage(ollamaToolError);
|
||||
writeLog("ERROR: "+e.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -505,9 +501,10 @@ public class Core {
|
||||
String message = responce.getJSONObject("message").getString("content");
|
||||
if(responce.getJSONObject("message").has("content") && !message.isBlank())
|
||||
{
|
||||
printMessageHandler.printMessage((supportColor?"\u001b[32m":"")+(LaunchOptions.getInstance().isShowFullMessage()? message : message.replaceAll("(?s)<think>.*?</think>", "")) +(supportColor?"\u001b[0m":""));
|
||||
OllamaMessage ollamaMessage = new OllamaMessage(OllamaMessageRole.ASSISTANT, message);
|
||||
printMessageHandler.printMessage(ollamaMessage);
|
||||
writeLog("Response content: "+ message);
|
||||
ollamaObject.addMessage(new OllamaMessage(OllamaMessageRole.ASSISTANT, message));
|
||||
ollamaObject.addMessage(ollamaMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,7 +538,7 @@ public class Core {
|
||||
if(files == null) {
|
||||
return;
|
||||
}
|
||||
PluginLoader loader = new PluginLoader();
|
||||
Loader loader = new Loader();
|
||||
for(File file : files) {
|
||||
try(FileSystem fs = FileSystems.newFileSystem(file.toPath())){
|
||||
if(!fs.getPath("/plugin.json").toFile().exists())
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package me.zacharias.chat.core;
|
||||
|
||||
import me.zacharias.chat.ollama.OllamaMessage;
|
||||
import me.zacharias.chat.ollama.OllamaMessageRole;
|
||||
|
||||
/**
|
||||
* Represents a {@link PrintAdvanceMessageHandler}.
|
||||
* This is used by the {@link Core} to print messages for the user, LLM, Tools, or potentialy System, see {@link OllamaMessageRole}.
|
||||
* For a simpler version see {@link PrintMessageHandler}
|
||||
*/
|
||||
public interface PrintAdvanceMessageHandler {
|
||||
/**
|
||||
* Expected to handle the printing of the provided {@link OllamaMessage}.
|
||||
* @param message The {@link OllamaMessage} requested to be printed from a veriity of sources, see {@link OllamaMessageRole}
|
||||
*/
|
||||
void printMessage(OllamaMessage message);
|
||||
|
||||
/**
|
||||
* 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 errorMessage The error message to be printed.
|
||||
* If color is not supported, it will print the message without color.
|
||||
*/
|
||||
void printErrorMessage(OllamaMessage errorMessage);
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
package me.zacharias.chat.core;
|
||||
|
||||
import me.zacharias.chat.ollama.OllamaMessage;
|
||||
|
||||
import static me.zacharias.chat.ollama.OllamaFunctionArgument.deconstructOllamaFunctionArguments;
|
||||
|
||||
/**
|
||||
* Represents a PrintMessageHandler.
|
||||
* This is used by the Core to print messages to the user or API Clients.
|
||||
* The Core uses this to print messages to the user or API Clients.
|
||||
*/
|
||||
public interface PrintMessageHandler {
|
||||
public interface PrintMessageHandler extends PrintAdvanceMessageHandler {
|
||||
/**
|
||||
* Handles the printing of a message.
|
||||
* This is meant to output the message to the user or API Client.
|
||||
@@ -12,6 +16,21 @@ public interface PrintMessageHandler {
|
||||
*/
|
||||
void printMessage(String message);
|
||||
|
||||
/**
|
||||
* This is a default implementation when wrapping {@link PrintAdvanceMessageHandler} to this "simpler" {@link PrintMessageHandler}
|
||||
*
|
||||
*
|
||||
* @param message The {@link OllamaMessage} requested to be printed from a veriity of sources, see {@link me.zacharias.chat.ollama.OllamaMessageRole}
|
||||
*/
|
||||
default void printMessage(OllamaMessage message) {
|
||||
printMessage(switch (message.getRole()) {
|
||||
case ASSISTANT -> (color()?"\u001b[32m":"")+(LaunchOptions.getInstance().isShowFullMessage()? message.getContent() : message.getContent().replaceAll("(?s)<think>.*?</think>", "")) +(color()?"\u001b[0m":"");
|
||||
case TOOL -> (color() ?"\u001b[31m":"")+message.getContent()+(color()?"\u001b[0m":"");
|
||||
case USER, SYSTEM -> (color() ?"\u001b[37m":"")+message.getContent()+(color()?"\u001b[0m":"");
|
||||
default -> throw new IllegalArgumentException("Invalid message role");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if color is supported by the PrintMessageHandler.
|
||||
* This uses ANSI escape codes to color the output.
|
||||
@@ -32,4 +51,17 @@ public interface PrintMessageHandler {
|
||||
printMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a default implementation when wrapping {@link PrintAdvanceMessageHandler} to this "simpler" {@link PrintMessageHandler}
|
||||
*
|
||||
*
|
||||
* 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 errorMessage The error message to be printed.
|
||||
* If color is not supported, it will print the message without color.
|
||||
*/
|
||||
default void printErrorMessage(OllamaMessage errorMessage) {
|
||||
printError(errorMessage.getContent());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,20 @@ public class OllamaMessage {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The role of who sent this "message", represented as {@link OllamaMessageRole}
|
||||
*/
|
||||
public OllamaMessageRole getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The "message" or content sent by a source
|
||||
*/
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
JSONObject json = new JSONObject();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
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;
|
||||
@@ -15,10 +14,6 @@ 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;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -452,6 +447,17 @@ public class OllamaObject {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the {@link FileHandler} singleton with the given base directory and adds its tools.
|
||||
* <p>
|
||||
* The {@link FileHandler} is intentionally constructed here rather than eagerly,
|
||||
* as file access tooling should only be initialized if explicitly requested during
|
||||
* object construction. Calling this after {@link #build()} is technically possible
|
||||
* but not intended.
|
||||
* </p>
|
||||
* @param baseDirectory the base directory for file access, see {@link FileHandlerLocation}
|
||||
* @return The {@link OllamaObjectBuilder}
|
||||
*/
|
||||
public OllamaObjectBuilder addFileTools(@MagicConstant(valuesFromClass = FileHandlerLocation.class) String baseDirectory)
|
||||
{
|
||||
new FileHandler(baseDirectory);
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
package me.zacharias.chat.plugin;
|
||||
|
||||
public class Plugin {
|
||||
private final PluginMetadata metadata = null;
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package me.zacharias.chat.plugin;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class PluginLoader {
|
||||
|
||||
public PluginLoader() {
|
||||
}
|
||||
|
||||
public Plugin loadPlugin(FileSystem pluginJar, JSONObject pluginMeta) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||
String pluginName = pluginMeta.getString("name");
|
||||
String pluginEntryPoint = pluginMeta.getString("entryPoint");
|
||||
String pluginVersion = pluginMeta.getString("version");
|
||||
String pluginDescription = pluginMeta.optString("description", "No description provided");
|
||||
String[] pluginAuthors = pluginMeta.optJSONArray("author") != null ?
|
||||
pluginMeta.getJSONArray("author").toList().toArray(new String[0]) : new String[0];
|
||||
|
||||
PluginMetadata pluginMetadata = new PluginMetadata() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return pluginName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return pluginVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return pluginDescription;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getAuthor() {
|
||||
return pluginAuthors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String entryPoint() {
|
||||
return pluginEntryPoint;
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
Plugin plugin = (Plugin) Class.forName(pluginEntryPoint).newInstance();
|
||||
Field metadataField = plugin.getClass().getDeclaredField("metadata");
|
||||
metadataField.set(plugin, pluginMetadata);
|
||||
if (metadataField.get(plugin) == null)
|
||||
throw new IllegalStateException("Plugin metadata field is null for plugin: " + pluginName);
|
||||
}catch (NoSuchFieldException e)
|
||||
{
|
||||
throw new IllegalStateException("Plugin class " + pluginEntryPoint + " does not have a 'metadata' field", e);
|
||||
}
|
||||
return null;
|
||||
//ClassLoader classLoader = pluginJar.
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package me.zacharias.chat.plugin;
|
||||
|
||||
public interface PluginMetadata {
|
||||
public String getName();
|
||||
public String getVersion();
|
||||
public String getDescription();
|
||||
public String[] getAuthor();
|
||||
public String entryPoint();
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package me.zacharias.chat.plugin.annotation.injectons;
|
||||
|
||||
import me.zacharias.chat.plugin.Plugin;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
|
||||
public @interface InjectPlugin {
|
||||
Class<? extends Plugin> classType() default Plugin.class;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package me.zacharias.chat.plugin.loader;
|
||||
|
||||
import jdk.jshell.spi.ExecutionControl;
|
||||
import me.zacharias.chat.core.Pair;
|
||||
import me.zacharias.chat.ollama.*;
|
||||
import me.zacharias.chat.plugin.Plugin;
|
||||
import me.zacharias.chat.plugin.PluginMetadata;
|
||||
import me.zacharias.chat.plugin.exceptions.ToolRuntimeException;
|
||||
import me.zacharias.chat.plugin.tool.Tool;
|
||||
import me.zacharias.chat.plugin.tool.ToolResponse;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Loader {
|
||||
private final ArrayList<Pair<Plugin, PluginMetadata>> plugins = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Mock example!
|
||||
* @return
|
||||
*/
|
||||
public OllamaTool getTool() {
|
||||
// Mock example!
|
||||
Tool tool = null;
|
||||
|
||||
return new OllamaFunctionTool() {
|
||||
@Override
|
||||
public @NotNull String name() {
|
||||
return tool.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return tool.description();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull OllamaPerameter parameters() {
|
||||
// TODO: make a wrapper around ToolPerameters to OllamaPeramer
|
||||
throw new RuntimeException(new ExecutionControl.NotImplementedException("To be Implemented"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public OllamaToolRespnce function(OllamaFunctionArgument... args) {
|
||||
// Wrap OllamaFunctionArguments[] to ether ToolArgument[] or ToolArguments(current implementation)
|
||||
try {
|
||||
ToolResponse toolResponse = tool.callTool(null);
|
||||
} catch (ToolRuntimeException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
// Wrap ToolResponce to an OllamaToolRespnce(Well I see a typo here now)
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// Replaces the toString which is used thru out the Core and OllamaObject to get the JSON representation of a Function Tool
|
||||
return tool.getToolJSON();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import me.zacharias.chat.ollama.OllamaPerameter;
|
||||
import me.zacharias.chat.ollama.OllamaToolRespnce;
|
||||
import me.zacharias.chat.plugin.annotation.OllamaTool;
|
||||
import me.zacharias.chat.plugin.annotation.injectons.InjectPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@OllamaTool
|
||||
public class Tool extends OllamaFunctionTool {
|
||||
@@ -13,7 +14,7 @@ public class Tool extends OllamaFunctionTool {
|
||||
Test core;
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
public @NotNull String name() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -23,7 +24,7 @@ public class Tool extends OllamaFunctionTool {
|
||||
}
|
||||
|
||||
@Override
|
||||
public OllamaPerameter parameters() {
|
||||
public @NotNull OllamaPerameter parameters() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,3 +25,9 @@ jar{
|
||||
attributes 'Main-Class': 'me.zacharias.chat.display.Main'
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(javaVersion)) // Set Java version
|
||||
}
|
||||
}
|
||||
@@ -22,3 +22,9 @@ dependencies {
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(javaVersion)) // Set Java version
|
||||
}
|
||||
}
|
||||
@@ -21,3 +21,9 @@ dependencies {
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(javaVersion)) // Set Java version
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package me.zacharias.chat.plugin;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public abstract class Data {
|
||||
public abstract File getDataDictionary();
|
||||
public abstract LoadedPlugins getLoadedPlugins();
|
||||
public abstract File getCacheDirectory();
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package me.zacharias.chat.plugin;
|
||||
|
||||
|
||||
import me.zacharias.chat.plugin.annotations.InjectPluginMetadata;
|
||||
import me.zacharias.chat.plugin.annotations.PluginEntryPoint;
|
||||
import me.zacharias.chat.plugin.tool.Tool;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Entry point for the plugin, this is required for the plugin to work, and you MUST annotate it with {@link PluginEntryPoint}
|
||||
*/
|
||||
public abstract class Plugin {
|
||||
|
||||
/**
|
||||
* This shuld be used if {@link PluginEntryPoint#requireData()} is false (default)
|
||||
* @apiNote While in this constructor, it is not safe to assume any plugin is loaded other than potentially defined plugins in {@link PluginEntryPoint#dependencies()}. See {@link this#onInit}
|
||||
*/
|
||||
public Plugin() {}
|
||||
|
||||
/**
|
||||
* This shuld be used if {@link PluginEntryPoint#requireData()} is true
|
||||
* @apiNote While in this constructor, it is not safe to assume any plugin is loaded other than potentially defined plugins in {@link PluginEntryPoint#dependencies()}. See {@link this#onInit}
|
||||
* @param data the {@link Data} generated by the plugin loader
|
||||
*/
|
||||
public Plugin(Data data) {}
|
||||
|
||||
/**
|
||||
* This will be called once every plugin has been loaded
|
||||
*/
|
||||
public void onInit(){}
|
||||
|
||||
/**
|
||||
* This will be called if the core gets a "fetal" exception or fails to fetch certain info
|
||||
*/
|
||||
public void onDisable(){}
|
||||
|
||||
/**
|
||||
* This will be called after all plugins have been initialised; here it is safe to perform potential stuff where you load data from optional dependencies, or similar things
|
||||
*/
|
||||
public void onEnable(){}
|
||||
|
||||
/**
|
||||
* The "main" method of a plugin, it will be the one returning a list of all tools you define.
|
||||
* @return An array of all {@link Tool}s you provide in this plugin, see {@link Tool} for definitions or <a href="https://git.server.4zellen.se/Chat_things/NeuroDock/src/branch/master/MALAPITool">MAL API tool</a> for ideas or examples
|
||||
*/
|
||||
public Tool[] getTools(){
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents your plugin's Metadata
|
||||
* @return A {@link PluginMetadata} object representing the metadata for your plugin
|
||||
*/
|
||||
@NotNull
|
||||
public abstract PluginMetadata getMetadata();
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package me.zacharias.chat.plugin;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Interface defining the core properties/information about the plugin
|
||||
*/
|
||||
public interface PluginMetadata {
|
||||
/**
|
||||
* Returns the name of the plugin
|
||||
* @return String containing the name
|
||||
*/
|
||||
@NotNull
|
||||
public String getName();
|
||||
|
||||
/**
|
||||
* Version of the project
|
||||
* @return String returning the version, expected format Major.Minor.Path see <a href="https://semver.org/">semver.org</a>
|
||||
*/
|
||||
@NotNull
|
||||
public String getVersion();
|
||||
|
||||
/**
|
||||
* Description for the tool, shuld be an explanatory, yet simple description
|
||||
* @return String containing the description
|
||||
*/
|
||||
default String getDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A String containing a list of all authors for this<br>
|
||||
* May be empty, but may not be null
|
||||
* @return
|
||||
*/
|
||||
@NotNull
|
||||
default String[] getAuthor() {
|
||||
return new String[0];
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -1,4 +1,4 @@
|
||||
package me.zacharias.chat.plugin.annotation;
|
||||
package me.zacharias.chat.plugin.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -6,7 +6,6 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface OllamaTool {
|
||||
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface InjectPluginMetadata {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package me.zacharias.chat.plugin.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface PluginEntryPoint {
|
||||
boolean requireData() default true;
|
||||
String[] dependencies() default {};
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package me.zacharias.chat.plugin.exceptions;
|
||||
|
||||
import me.zacharias.chat.plugin.tool.Tool;
|
||||
|
||||
public class ToolRuntimeException extends Exception {
|
||||
String toolName;
|
||||
|
||||
public String getToolName() {
|
||||
return toolName;
|
||||
}
|
||||
|
||||
public ToolRuntimeException(Tool tool, String message) {
|
||||
super(message);
|
||||
this.toolName = tool.name();
|
||||
}
|
||||
|
||||
public ToolRuntimeException(String toolName, String message) {
|
||||
super(message);
|
||||
this.toolName = toolName;
|
||||
}
|
||||
|
||||
public ToolRuntimeException(String toolName, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.toolName = toolName;
|
||||
}
|
||||
|
||||
public ToolRuntimeException(Tool tool, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.toolName = tool.name();
|
||||
}
|
||||
|
||||
public ToolRuntimeException(Tool tool, Throwable cause) {
|
||||
super(cause);
|
||||
this.toolName = tool.name();
|
||||
}
|
||||
|
||||
public ToolRuntimeException(String toolName, Throwable cause) {
|
||||
super(cause);
|
||||
this.toolName = toolName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package me.zacharias.chat.plugin.tool;
|
||||
|
||||
import me.zacharias.chat.plugin.Plugin;
|
||||
import me.zacharias.chat.plugin.exceptions.ToolRuntimeException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public abstract class Tool {
|
||||
|
||||
/**
|
||||
* The parent plugin of this tool, used by {@link Tool#getToolJSON()}.
|
||||
*/
|
||||
protected final Plugin PLUGIN;
|
||||
|
||||
/**
|
||||
* A base contructor, as the {@link Plugin} is used within this class
|
||||
* @param plugin The plugin that owns this tool
|
||||
*/
|
||||
public Tool(Plugin plugin) {
|
||||
this.PLUGIN = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* The core uses this method when generating the list of available functions to the LLM
|
||||
* @return a string formated JSON containing the representation of the tool definitions
|
||||
*/
|
||||
public final String getToolJSON() {
|
||||
JSONObject ret = new JSONObject();
|
||||
ret.put("type", "function");
|
||||
|
||||
JSONObject function = new JSONObject();
|
||||
function.put("name", String.format("%s_%s_RPCP", name(), PLUGIN.getMetadata().getName()).replaceAll(" ", "_"));
|
||||
if(description() != null) {
|
||||
function.put("description", description());
|
||||
}
|
||||
function.put("parameters", (parameters() == null? new JSONObject() : new JSONObject(parameters().toString())));
|
||||
|
||||
ret.put("function", function);
|
||||
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Name of the tool.</p>
|
||||
* @apiNote This is a lower snake_case formated string.
|
||||
* <p>Example: {@code "get_clock"}</p>
|
||||
* @return returns the name of this tool
|
||||
*/
|
||||
@NotNull
|
||||
abstract public String name();
|
||||
|
||||
/**
|
||||
* Description for the LLM on what this tool does
|
||||
* @return Short description defining the scope of this tool
|
||||
*/
|
||||
public String description() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The parameters this tool allows
|
||||
* @apiNote Can return {@link ToolParameters#empty()} if no arguments are needed
|
||||
* @return A {@link ToolParameters} object defining what this tool expects when being called
|
||||
*/
|
||||
@NotNull
|
||||
abstract public ToolParameters parameters();
|
||||
|
||||
/**
|
||||
* This is the main course of the tool. The LLM will call this on request of using your tool
|
||||
* @param arguments A collective of your {@link ToolParameters} from {@link Tool#parameters()}
|
||||
* @return The expected results, or {@link ToolResponse#empty(Tool)} if this is a "void" function
|
||||
* @throws ToolRuntimeException This indicates that an {@link Exception} is not fatal, any other exceptions thrown from this tool will be treated as fatal, and will result in this tool, or it's plugin being disabled
|
||||
*/
|
||||
abstract public ToolResponse callTool(ToolArguments arguments) throws ToolRuntimeException;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package me.zacharias.chat.plugin.tool;
|
||||
|
||||
public class ToolArguments {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package me.zacharias.chat.plugin.tool;
|
||||
|
||||
public class ToolParameters {
|
||||
/**
|
||||
* An empty {@link ToolParameters} for when a tool dosent need any perameters
|
||||
* @return the default {@link ToolParameters}
|
||||
*/
|
||||
public static ToolParameters empty() {
|
||||
return new ToolParameters();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package me.zacharias.chat.plugin.tool;
|
||||
|
||||
public class ToolResponse {
|
||||
public static ToolResponse empty(Tool tool) {
|
||||
return new ToolResponse();
|
||||
}
|
||||
|
||||
public static ToolResponse empty(String toolName)
|
||||
{
|
||||
return new ToolResponse();
|
||||
}
|
||||
}
|
||||
@@ -21,3 +21,9 @@ dependencies {
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(javaVersion)) // Set Java version
|
||||
}
|
||||
}
|
||||
+18
-1
@@ -21,7 +21,6 @@ subprojects {
|
||||
apply plugin: 'com.gradleup.shadow'
|
||||
|
||||
dependencies {
|
||||
implementation("org.json:json:20250107")
|
||||
implementation("org.jetbrains:annotations:23.1.0")
|
||||
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
|
||||
@@ -73,4 +72,22 @@ subprojects {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
maven(MavenPublication) {
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
name = "gitea"
|
||||
url = "https://git.server.4zellen.se/api/packages/Chat_things/maven"
|
||||
credentials {
|
||||
username = project.findProperty("giteaUser")
|
||||
password = project.findProperty("giteaToken")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
# This is used in sub-project that dosent explicitly require the API sub-project to only include if thay shuld be included project wide.
|
||||
useAPI = true
|
||||
javaVersion = 26
|
||||
@@ -15,3 +15,9 @@ jar{
|
||||
attributes 'Main-Class': 'me.zacharias.chat.launcher.Launcher'
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(javaVersion)) // Set Java version
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -1,4 +1,4 @@
|
||||
rootProject.name = 'AI-test'
|
||||
rootProject.name = 'neurodock'
|
||||
include 'API', 'Core', 'Display', 'launcher'
|
||||
|
||||
|
||||
@@ -13,3 +13,4 @@ include 'MALAPITool'
|
||||
include 'GeniusAPI'
|
||||
|
||||
include 'WikipediaTool'
|
||||
include 'Plugin-API'
|
||||
Reference in New Issue
Block a user