Some small poking at things :)

dont midn the version bump, it's easier to bump versions then to get Maven to bahave
This commit is contained in:
2026-06-28 15:46:57 +02:00
parent 6d7088fd76
commit 3c6a75a587
13 changed files with 377 additions and 92 deletions
+1 -40
View File
@@ -7,46 +7,7 @@
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" />
</component>
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="org.jetbrains.annotations.Nullable" />
<option name="myDefaultNotNull" value="org.jetbrains.annotations.NotNull" />
<option name="myOrdered" value="false" />
<option name="myNullables">
<value>
<list size="11">
<item index="0" class="java.lang.String" itemvalue="org.jspecify.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="2" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
<item index="3" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
<item index="4" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
<item index="5" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.Nullable" />
<item index="6" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="7" class="java.lang.String" itemvalue="jakarta.annotation.Nullable" />
<item index="8" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="9" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
<item index="10" class="java.lang.String" itemvalue="org.springframework.lang.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="11">
<item index="0" class="java.lang.String" itemvalue="org.jspecify.annotations.NonNull" />
<item index="1" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="2" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
<item index="3" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
<item index="4" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
<item index="5" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.NonNull" />
<item index="6" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="7" class="java.lang.String" itemvalue="jakarta.annotation.Nonnull" />
<item index="8" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="9" class="java.lang.String" itemvalue="lombok.NonNull" />
<item index="10" class="java.lang.String" itemvalue="org.springframework.lang.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_26" default="true" project-jdk-name="26" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" default="true" project-jdk-name="25" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
+1 -1
View File
@@ -2,7 +2,7 @@ plugins {
id 'java-library'
}
version = '1.6'
version = '1.6.6'
dependencies {
implementation project(":Plugin-API")
+36 -12
View File
@@ -40,6 +40,7 @@ public class Core {
* The file to write the logs to.
*/
private static File logFile = new File("./logs/latest.log");
private static File logDir = new File("./logs");
/**
* The writer to write the logs to.
*/
@@ -114,19 +115,33 @@ public class Core {
static {
// This should not be enforced like this, instead this should read a data field, or wait for an init to be called....
// Unsure of how to properly do this at this time, however.
setDataDirectory("AI-Chat");
// After looking at things, just run {@link Core.setDataDirectory(String)} somewhere else before initilising the Core object and seems to be fine
setDataDirectory("AI-Chat", false);
}
public static void setLogDirectory(String logDirectory)
{
logDir = new File(logDirectory);
if (!logDir.exists())
{
logDir.mkdir();
}
logFile = new File(logDirectory, "latest.log");
}
/**
* Set the data directory in appropriate locations depending on the host OS, falling back to $WORKING_DIR/data
* @param dataDirectory the data directory to use
*/
public static void setDataDirectory(String dataDirectory) {
public static void setDataDirectory(String dataDirectory, boolean fullDirectory) {
String data;
if(System.getenv("AI_CHAT_DEBUG") != null) {
data = "./data";
}
if(fullDirectory) {
data = dataDirectory;
}
else if(System.getProperty("os.name").toLowerCase().contains("windows")) {
String localappdata = System.getenv("LOCALAPPDATA");
if(localappdata == null) {
@@ -170,9 +185,8 @@ public class Core {
* rather than being relative to the working directory.
*/
private void initDirectories() {
ensureDir("./logs/");
ensureDir("./pythonFiles/");
ensureDir("./messages/");
ensureDir(logDir.getAbsolutePath());
ensureDir(DATA_DIR.getAbsolutePath() + "/messages");
}
/**
@@ -180,7 +194,7 @@ public class Core {
*
* @param path The path of the directory to create
*/
private static void ensureDir(String path) {
public static void ensureDir(String path) {
File dir = new File(path);
if (!dir.exists()) dir.mkdir();
}
@@ -206,6 +220,9 @@ public class Core {
*/
private void initLogWriter() {
try {
if(!logDir.exists()) {
logDir.mkdir();
}
if (logFile.exists()) {
rotateLogFile();
}
@@ -228,8 +245,8 @@ public class Core {
String line = br.readLine();
if (line != null) {
String date = line.substring(0, line.indexOf(">")).replaceAll("[/:]", "-");
logFile.renameTo(new File(logFile.getParentFile(), date + ".log"));
logFile = new File("./logs/latest.log");
logFile.renameTo(new File(logDir, date + ".log"));
logFile = new File(logDir,"/latest.log");
} else {
System.out.println("Existing log file is empty, overwriting it!");
logFile.delete();
@@ -296,7 +313,7 @@ public class Core {
JSONArray messages = buildMessagesArray();
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd_HH-mm-ss"));
writeMessagesTo(new File("./messages/" + timestamp + ".json"), messages);
writeMessagesTo(new File(DATA_DIR.getAbsolutePath()+"/messages/" + timestamp + ".json"), messages);
writeMessagesTo(new File(DATA_DIR, "messages.json"), messages);
}
@@ -712,7 +729,7 @@ public class Core {
*/
private OllamaFunctionTool findTool(JSONObject function) {
return funtionTools.stream()
.filter(f -> (f.getKey().name() + f.getValue())
.filter(f -> (f.getKey().name() + "_" + f.getValue())
.equalsIgnoreCase(function.getString("name")))
.map(Pair::getKey)
.findFirst()
@@ -834,12 +851,12 @@ public class Core {
Loader loader = new Loader();
for(File file : files) {
try(JarFile jar = new JarFile(file)){
JarEntry pluginJsonFile = jar.getJarEntry("/plugin.json");
JarEntry pluginJsonFile = jar.getJarEntry("plugin.json");
if(pluginJsonFile == null)
{
throw new PluginLoadingException("Plugin does not contain a plugin.json file", file.getName());
}
if(jar.getJarEntry("/me/neurodock/plugin/Plugin") != null)
if(jar.getJarEntry("me/neurodock/plugin/Plugin") != null)
throw new PluginLoadingException("Plugin bundles NeuroDock API classes. Consider using compileOnly", file.getName());
StringBuilder pluginJsonData = new StringBuilder();
String tmp = null;
@@ -857,6 +874,13 @@ public class Core {
throw new RuntimeException(e);
}
}
data.plugins.forEach(loadedPlugin -> {
for(OllamaFunctionTool tool : loader.getTools(loadedPlugin.plugin()))
{
addTool(tool, Source.RPCP);
}
});
}
private static class LoaderPluginData extends Data {
@@ -181,16 +181,17 @@ public class CoreMemory {
*/
public String getMappedMemories() {
ArrayList<String> memories = new ArrayList<>();
for (String key : memory.keySet()) {
memories.add(key + ": " + memory.getString(key));
for (String key : memory.getJSONObject(MAPPED_MEMORY).keySet()) {
memories.add(key + ": " + memory.getJSONObject(MAPPED_MEMORY).getString(key));
}
return new JSONArray(memories).toString();
}
public ArrayList<String> getMemoriesArray() {
ArrayList<String> memories = new ArrayList<>();
for (String key : memory.keySet()) {
memories.add(key + ": " + memory.getString(key));
int length = memory.getJSONArray(ARRAYED_MEMORY).length();
for (int i = 0; i < length; i++) {
memories.add(i + ": " + memory.getJSONArray(ARRAYED_MEMORY).getString(i));
}
return memories;
}
@@ -40,6 +40,13 @@ public record OllamaFunctionArgument(String argument, Object value) {
return value;
}
public <T extends Enum<T>> T getValue(Class<T> enumClass) {
if(value instanceof String str) {
return Enum.valueOf(enumClass, str);
}
throw new IllegalArgumentException(String.format("%s is not a valid enum value of type %s", value.toString(), enumClass.getName()));
}
public static String deconstructOllamaFunctionArgument(OllamaFunctionArgument argument) {
return argument.argument() + ": " + argument.value();
}
@@ -25,7 +25,7 @@ public abstract class OllamaFunctionTool implements OllamaTool {
ret.put("type", "function");
JSONObject function = new JSONObject();
function.put("name", name()+(source != null ? source : ""));
function.put("name", name()+"_"+(source != null ? source : ""));
if(description() != null) {
function.put("description", description());
}
@@ -1,10 +1,10 @@
package me.neurodock.ollama;
import me.neurodock.plugin.tool.ToolParameters;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import java.util.stream.Stream;
/**
* Represents the parameters of a tool.
@@ -36,11 +36,11 @@ public class OllamaPerameter {
/**
* the properties of the parameters
*/
JSONObject properties;
private final JSONObject properties;
/**
* the required parameters
*/
String[] required;
private final String[] required;
/**
* Gets the properties of the {@link OllamaPerameter}
@@ -122,6 +122,35 @@ public class OllamaPerameter {
return this;
}
public <T extends Enum<T>> OllamaPerameterBuilder addEnumProperty(String name, Class<T> enumClass, String description, boolean required) {
StringBuilder newDescription = new StringBuilder(description);
if(newDescription.charAt(newDescription.length() - 1) != '.') {
newDescription.append(". ");
}
else
{
newDescription.append(' ');
}
newDescription.append("Enum values: ");
Iterator<T> it = Arrays.stream(enumClass.getEnumConstants()).iterator();
while(it.hasNext()) {
T item = it.next();
newDescription.append(item.name());
if(it.hasNext()) {
newDescription.append(", ");
}
}
return addProperty(name, Type.ENUM, newDescription.toString(), required);
}
public <T extends Enum<T>> OllamaPerameterBuilder addEnumProperty(Class<T> enumClass, String description) {
return addEnumProperty(enumClass.getSimpleName(), enumClass, description, false);
}
/**
* Makes a previusly optinal perameter required for this {@link OllamaPerameterBuilder}
* @param name The name of the parameter
@@ -143,6 +172,22 @@ public class OllamaPerameter {
return this;
}
/**
* Coverts a RPCP Tool Perameter to a CTP Ollama Peameter
* @param parameters
* @return
*/
public OllamaPerameterBuilder of(ToolParameters parameters) {
required.addAll(Arrays.asList(parameters.getRequired()));
for(String key : parameters.getProperties().keySet())
{
Property property = new Property(parameters.getProperties().getJSONObject(key).getString("type"),
parameters.getProperties().getJSONObject(key).getString("description"));
propertyMap.put(key, property);
}
return this;
}
/**
* Builds the {@link OllamaPerameter}
* @return The {@link OllamaPerameter}
@@ -238,6 +283,19 @@ public class OllamaPerameter {
Type(String type) {
this.type = type;
}
Type of(String type)
{
return switch (type) {
case "string" -> STRING;
case "int" -> INT;
case "boolean" -> BOOLEAN;
case "enum" -> ENUM;
case "array" -> ARRAY;
case "object" -> OBJECT;
default -> throw new IllegalArgumentException("Unknown type " + type);
};
}
}
}
}
@@ -48,7 +48,7 @@ public class Loader {
tools.add(new OllamaFunctionTool() {
@Override
public @NotNull String name() {
return tool.name();
return tool.name() + "_" + plugin.getMetadata().getName();
}
@Override
@@ -58,8 +58,7 @@ public class Loader {
@Override
public @NotNull OllamaPerameter parameters() {
// TODO: make a wrapper around ToolPerameters to OllamaPeramer
throw new RuntimeException(new ExecutionControl.NotImplementedException("To be Implemented"));
return OllamaPerameter.builder().of(tool.parameters()).build();
}
@Override
@@ -98,7 +97,7 @@ public class Loader {
throw new PluginLoadingException("Malformed plugin json", UNKNOWN_PLUGIN);
if(!pluginJson.has("entryPoint") || !(pluginJson.get("entryPoint") instanceof String pluginEntryPoint))
throw new PluginLoadingException("Malformed plugin json", pluginName);
JarEntry entry = jar.getJarEntry(pluginEntryPoint.replaceAll("\\.", "/"));
JarEntry entry = jar.getJarEntry(pluginEntryPoint.replaceAll("\\.", "/")+".class");
if(entry == null) throw new PluginLoadingException("Missing plugin entrypoint", pluginName);
Plugin plugin = null;
@@ -146,6 +145,6 @@ public class Loader {
);
}
return new LoadedPlugin(plugin, classLoader, null/*TODO: MUST BE REPLACED WITH ACTUAL PLUGIN METADATA! this is read from the PluginEntyPoint*/);
return new LoadedPlugin(plugin, classLoader, plugin.getMetadata()/*TODO: MUST BE REPLACED WITH ACTUAL PLUGIN METADATA! this is read from the PluginEntyPoint*/);
}
}
@@ -32,6 +32,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import static me.neurodock.core.Core.ensureDir;
import static me.neurodock.core.Core.writeLog;
/**
@@ -71,6 +72,8 @@ public class PythonRunner extends OllamaFunctionTool {
public PythonRunner(Core core) {
this.core = core;
ensureDir("./pythonFiles/");
try {
serverSocket = new ServerSocket(6050);
Thread thread = new Thread(() -> {
+1 -1
View File
@@ -2,7 +2,7 @@ plugins {
id 'java-library'
}
version = '0.1.2'
version = '0.1.4.1'
repositories {
mavenCentral()
@@ -38,7 +38,7 @@ public abstract class Tool {
if(description() != null) {
function.put("description", description());
}
function.put("parameters", (parameters() == null? new JSONObject() : new JSONObject(parameters().toString())));
function.put("parameters", (parameters() == null? new JSONObject() : new JSONObject(parameters().getJSON())));
ret.put("function", function);
@@ -1,12 +1,242 @@
package me.neurodock.plugin.tool;
public class ToolParameters {
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* An empty {@link ToolParameters} for when a tool dosent need any perameters
* @return the default {@link ToolParameters}
* Represents the parameters of a tool.
* This is used by Ollama to determine the parameters of a tool.
*/
public static ToolParameters empty() {
return new ToolParameters();
public class ToolParameters {
/**
* Creates a new instance of {@link ToolParameters}.
* @param properties The properties of the parameters
* @param required The required parameters
*/
private ToolParameters(JSONObject properties, String[] required) {
this.properties = properties;
this.required = required;
};
public JSONObject getJSON() {
JSONObject json = new JSONObject();
json.put("type", "object");
json.put("properties", properties);
json.put("required", required);
return json;
}
/**
* the properties of the parameters
*/
JSONObject properties;
/**
* the required parameters
*/
String[] required;
/**
* Gets the properties of the {@link ToolParameters}
* @return The properties of the {@link ToolParameters}
*/
public JSONObject getProperties() {
return properties;
}
/**
* Gets the required parameters of the {@link ToolParameters}
* @return The required parameters of the {@link ToolParameters}
*/
public String[] getRequired() {
return required;
}
/**
* Creates a new instance of {@link ToolParametersBuilder}.
* @return The {@link ToolParametersBuilder}
*/
public static ToolParametersBuilder builder() {
return new ToolParametersBuilder();
}
/**
* Creates an empty {@link ToolParameters}
* @return an empty {@link ToolParameters}
* @apiNote This is equvalent to
* <pre>{@code
* ToolParameters.builder().build();
* }</pre>
*/
public static ToolParameters empty() {
return builder().build();
}
/**
* Represents a builder for {@link ToolParameters}.
*/
public static class ToolParametersBuilder {
/**
* The properties of the parameters.
*/
Map<String, Property> propertyMap = new HashMap<>();
/**
* The required parameters.
*/
ArrayList<String> required = new ArrayList<>();
/**
* Add an optinal perameter to this {@link ToolParametersBuilder}
* @param name The name of the parameter
* @param type The type of the parameter
* @param description The description of the parameter
* @return The {@link ToolParametersBuilder}
* @apiNote Prefer {@link #addProperty(String, Type, String, boolean)} to be explicit about required state
*/
public ToolParametersBuilder addProperty(String name, Type type, String description) {
return ToolParametersBuilder.this.addProperty(name, type, description, false);
}
/**
* Add a potentialy required peremeter to this {@link ToolParametersBuilder}.
* @param name The name of the parameter
* @param type The type of the parameter
* @param description The description of the parameter
* @param required The required state of the parameter
* @return The {@link ToolParametersBuilder}
*/
public ToolParametersBuilder addProperty(String name, Type type, String description, boolean required) {
if(name == null || type == null || description == null) {
return this;
}
propertyMap.put(name, new Property(type.getType(), description));
if(required) {
this.required.add(name);
}
return this;
}
/**
* Makes a previusly optinal perameter required for this {@link ToolParametersBuilder}
* @param name The name of the parameter
* @return The {@link ToolParametersBuilder}
*/
public ToolParametersBuilder required(String name) {
required.add(name);
return this;
}
/**
* Removes a property from the parameters.
* @param name The name of the property to remove
* @return The {@link ToolParametersBuilder}
*/
public ToolParametersBuilder removeProperty(String name) {
propertyMap.remove(name);
required.remove(name);
return this;
}
/**
* Builds the {@link ToolParameters}
* @return The {@link ToolParameters}
*/
public ToolParameters build() {
JSONObject properties = new JSONObject();
for(String name : propertyMap.keySet()) {
properties.put(name, new JSONObject(propertyMap.get(name).toString()));
}
return new ToolParameters(properties, required.toArray(new String[0]));
}
/**
* Represents a property of a parameter.
*/
private static class Property {
/**
* The type of the property.
*/
String type;
/**
* The description of the property.
*/
String description;
/**
* Creates a new instance of {@link Property}.
* @param type The type of the property
* @param description The description of the property
*/
public Property(String type, String description) {
this.type = type;
this.description = description;
}
@Override
public String toString() {
JSONObject json = new JSONObject();
json.put("type", type);
json.put("description", description);
return json.toString();
}
}
/**
* Represents the type of parameter.
*/
public enum Type {
/**
* Represents a string parameter.
*/
STRING("string"),
/**
* Represents an integer parameter.
*/
INT("int"),
/**
* Represents a boolean parameter.
*/
BOOLEAN("boolean"),
/**
* Represents a enum parameter.
*/
ENUM("enum"),
/**
* Represents a array parameter.
*/
ARRAY("array"),
/**
* Represents a object parameter.
*/
OBJECT("object");
/**
* The type of the parameter.
*/
private final String type;
/**
* Gets the type of the parameter.
* @return The type of the parameter
*/
public String getType() {
return type;
}
/**
* Creates a new instance of {@link Type}.
* @param type The type of the parameter
*/
Type(String type) {
this.type = type;
}
}
}
}
+3 -1
View File
@@ -70,7 +70,7 @@ subprojects {
if (tasks.findByName('javadocJar')) artifact tasks.javadocJar
pom {
name = proj.name
description = "${proj.name} module of AI-test"
description = "${proj.name} module of NeuroDock"
}
}
}
@@ -98,4 +98,6 @@ subprojects {
}
}
}
java.toolchain.languageVersion = JavaLanguageVersion.of(25)
}