Basicly refactord the entire project into a modular nature

This commit is contained in:
2025-02-20 23:28:24 +01:00
parent 6935e938a3
commit b892306c09
26 changed files with 340 additions and 145 deletions

4
.idea/gradle.xml generated
View File

@@ -9,6 +9,10 @@
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/API" />
<option value="$PROJECT_DIR$/Core" />
<option value="$PROJECT_DIR$/Display" />
<option value="$PROJECT_DIR$/luancher" />
</set>
</option>
</GradleProjectSettings>

20
API/build.gradle Normal file
View File

@@ -0,0 +1,20 @@
plugins {
id 'java'
}
group = 'me.zacharias'
version = '1.0-SNAPSHOT'
dependencies {
implementation project(":Core")
}
test {
useJUnitPlatform()
}
jar{
manifest {
attributes 'Main-Class': 'me.zacharias.char.api.APIServer'
}
}

View File

@@ -0,0 +1,12 @@
package me.zacharias.chat.api;
public enum APIEndpoints {
// API request endpoints
ADD_TOOL,
QUERY,
// API response endpoints
RESPONSE,
USE_TOOL,
}

View File

@@ -0,0 +1,13 @@
package me.zacharias.chat.api;
import java.net.ServerSocket;
import java.util.ArrayList;
public class APIServer {
ArrayList<Client> clientsList = new ArrayList<>();
ServerSocket serverSocket;
public APIServer(int port, String redirectedOutput) {
}
}

View File

@@ -0,0 +1,4 @@
package me.zacharias.chat.api;
public class Client {
}

16
Core/build.gradle Normal file
View File

@@ -0,0 +1,16 @@
plugins {
id 'java-library'
}
group = 'me.zacharias'
version = '1.0-SNAPSHOT'
dependencies {
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21)) // Set Java version
}
}

View File

@@ -1,4 +1,4 @@
package me.zacharias.chat;
package me.zacharias.chat.core;
import me.zacharias.chat.ollama.*;
import me.zacharias.chat.ollama.exceptions.OllamaToolErrorException;
@@ -7,25 +7,28 @@ import org.json.JSONObject;
import java.io.*;
import java.net.*;
import java.nio.file.Files;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Main {
private String ollamaIP = "localhost";//"192.168.5.184";
private int ollamaPort = 11434;
private URL url;
public class Core {
private static File logFile = new File("./logs/latest.log");
private static BufferedWriter logWriter;
private ScheduledExecutorService scheduler;
private OllamaObject ollamaObject;
private ArrayList<OllamaFuntionTool> funtionTools;
private ArrayList<OllamaFuntionTool> funtionTools = new ArrayList<>();
private String ollamaIP = "localhost";//"192.168.5.184";
private int ollamaPort = 11434;
private URL url;
private final PrintMessageHandler printMessageHandler;
private boolean supportColor;
{
File dir = new File("./logs/");
@@ -79,14 +82,7 @@ public class Main {
e.printStackTrace();
}
}, 0, 3, TimeUnit.MINUTES);
}
public static void main(String[] args) {
new Main();
}
public Main()
{
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
scheduler.shutdownNow();
try {
@@ -113,91 +109,32 @@ public class Main {
throw new RuntimeException(e);
}
}));
ollamaObject = OllamaObject.builder()
.setModel("llama3-AI")
.keep_alive(10)
.addTool(new TimeTool())
.addTool(new PythonRunner())
.stream(false)
.build();
writeLog("Creating base OllamaObject with model: "+ollamaObject.getModel());
funtionTools = new ArrayList<>();
System.out.println("Installed tools");
writeLog("Tools installed in this instance");
for(OllamaTool tool : ollamaObject.getTools())
{
if(tool instanceof OllamaFuntionTool funtion)
{
StringBuilder args = new StringBuilder();
OllamaPerameter perameter = funtion.parameters();
if(perameter != null) {
JSONObject obj = perameter.getProperties();
for (String name : obj.keySet()) {
args.append(args.toString().isBlank() ? "" : ", ").append(obj.getJSONObject(name).getString("type")).append(Arrays.stream(perameter.getRequired()).anyMatch(str -> str.equalsIgnoreCase(name)) ? "" : "?").append(" ").append(name);
}
}
System.out.println("> Function: "+funtion.name()+"("+args+")");
writeLog("Function: "+funtion.name()+"("+args+")");
funtionTools.add(funtion);
}
}
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Message Trsnscription:");
try {
while (true) {
System.out.print("> ");
StringBuilder message = new StringBuilder(br.readLine());
while(br.ready())
{
message.append("\n").append(br.readLine());
}
if(message.toString().startsWith("/"))
{
switch (message.substring(1)) {
case "help":
System.out.print("""
Available commands:
/help Prints this help message.
/bye Exits the program.
/write Flushes the current log stream to file.
""");
break;
case "bye":
writeLog("Exiting program...");
System.out.println("Bye!");
System.exit(0);
return;
case "write":
logWriter.flush();
break;
default:
System.out.println("Unknown command: "+message);
}
}
else
{
writeLog("User: "+message);
ollamaObject.addMessage(new OllamaMessage(OllamaMessageRole.USER, message.toString()));
//System.out.println(ollamaObject.toString());
handleResponce(qurryOllama(ollamaObject));
}
}
}catch (Exception e) {
e.printStackTrace();
System.out.println("Exiting due to exception");
System.exit(-1);
}
}
public JSONObject qurryOllama(OllamaObject request)
public Core(PrintMessageHandler printMessageHandler) {
this.printMessageHandler = printMessageHandler;
supportColor = printMessageHandler.color();
}
public void setOllamaObject(OllamaObject ollamaObject) {
this.ollamaObject = ollamaObject;
}
public OllamaObject getOllamaObject() {
return ollamaObject;
}
public void addFuntionTool(OllamaFuntionTool funtionTool) {
funtionTools.add(funtionTool);
}
public static void flushLog() {
try {
logWriter.flush();
}catch (IOException e) {}
}
public JSONObject qurryOllama()
{
try {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
@@ -207,7 +144,7 @@ public class Main {
connection.setConnectTimeout(80*1000);
try(DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) {
wr.writeBytes(request.toString());
wr.writeBytes(ollamaObject.toString());
wr.flush();
}
@@ -234,7 +171,7 @@ public class Main {
}
else {
connection.disconnect();
System.out.println("Error: HTTP Response code - " + responseCode + "\n"+response.toString());
System.err.println("Error: HTTP Response code - " + responseCode + "\n"+response.toString());
throw new RuntimeException("HTTP Response code - " + responseCode);
}
} catch (IOException e) {
@@ -261,7 +198,7 @@ public class Main {
if(functions.isEmpty()) {
ollamaObject.addMessage(new OllamaToolError("Function '"+function.getString("name")+"' does not exist"));
System.out.println(">> \u001b[31mTried funtion call "+function.getString("name")+" but failed to find it.\u001b[0m");
printMessageHandler.printMessage((supportColor ?"\u001b[31m":"")+"Tried funtion call "+function.getString("name")+" but failed to find it."+(printMessageHandler.color()?"\u001b[0m":""));
writeLog("Failed function call to "+function.getString("name"));
}
else {
@@ -279,31 +216,30 @@ public class Main {
try {
OllamaToolRespnce function1 = func.function(argumentArrayList.toArray(new OllamaFunctionArgument[0]));
ollamaObject.addMessage(function1);
System.out.println(">> \u001b[34mCall " + func.name() + "\u001b[0m");
printMessageHandler.printMessage((supportColor?"\u001b[34m":"")+"Call "+func.name() + (printMessageHandler.color()?"\u001b[0m":""));
writeLog("Successfully function call " + func.name() + " output: " + function1.getResponse());
} catch (OllamaToolErrorException e) {
ollamaObject.addMessage(new OllamaToolError(e.getMessage()));
System.out.println(">> \u001b[31mTried funtion call " + func.name() + " but failed due to " + e.getError() + "\u001b[0m");
printMessageHandler.printMessage((supportColor?"\u001b[31m":"")+"Tried funtion call " + func.name() + " but failed due to " + e.getError() + (supportColor?"\u001b[0m":""));
writeLog(e.getMessage());
}
}
}
}
}
if(responce.getJSONObject("message").has("content") && !responce.getJSONObject("message").getString("content").isBlank())
{
System.out.println(">> \u001b[32m"+responce.getJSONObject("message").getString("content")+"\u001b[0m");
writeLog("Response content: "+responce.getJSONObject("message").getString("content"));
ollamaObject.addMessage(new OllamaMessage(OllamaMessageRole.ASSISTANT, responce.getJSONObject("message").getString("content")));
}
handleResponce(qurryOllama(ollamaObject));
}
else if(responce.getJSONObject("message").has("content") && !responce.getJSONObject("message").getString("content").isBlank())
{
System.out.println(">> \u001b[32m"+responce.getJSONObject("message").getString("content")+"\u001b[0m");
writeLog("Response content: "+responce.getJSONObject("message").getString("content"));
ollamaObject.addMessage(new OllamaMessage(OllamaMessageRole.ASSISTANT, responce.getJSONObject("message").getString("content")));
checkIfResponceMessage(responce);
handleResponce(qurryOllama());
}
else checkIfResponceMessage(responce);
}
}
private void checkIfResponceMessage(JSONObject responce) {
if(responce.getJSONObject("message").has("content") && !responce.getJSONObject("message").getString("content").isBlank())
{
printMessageHandler.printMessage((supportColor?"\u001b[32m":"")+responce.getJSONObject("message").getString("content")+(supportColor?"\u001b[0m":""));
writeLog("Response content: "+responce.getJSONObject("message").getString("content"));
ollamaObject.addMessage(new OllamaMessage(OllamaMessageRole.ASSISTANT, responce.getJSONObject("message").getString("content")));
}
}

View File

@@ -0,0 +1,6 @@
package me.zacharias.chat.core;
public interface PrintMessageHandler {
void printMessage(String message);
boolean color();
}

View File

@@ -14,7 +14,7 @@ public class OllamaMessage {
@Override
public String toString() {
JSONObject json = new JSONObject();
json.put("role", role);
json.put("role", role.getRole());
json.put("content", content);
return json.toString();
}

View File

@@ -1,7 +1,5 @@
package me.zacharias.chat.ollama.exceptions;
import me.zacharias.chat.ollama.OllamaToolRespnce;
public class OllamaToolErrorException extends RuntimeException {
private final String tool;
private final String error;

20
Display/build.gradle Normal file
View File

@@ -0,0 +1,20 @@
plugins {
id 'java'
}
group = 'me.zacharias'
version = '1.0-SNAPSHOT'
dependencies {
implementation project(":Core")
}
test {
useJUnitPlatform()
}
jar{
manifest {
attributes 'Main-Class': 'me.zacharias.chat.display.Main'
}
}

View File

@@ -0,0 +1,106 @@
package me.zacharias.chat.display;
import me.zacharias.chat.core.Core;
import me.zacharias.chat.core.PrintMessageHandler;
import me.zacharias.chat.ollama.*;
import org.json.JSONObject;
import java.io.*;
import java.util.*;
import static me.zacharias.chat.core.Core.writeLog;
public class Display {
Core core = new Core(new PrintMessageHandler() {
@Override
public void printMessage(String message) {
System.out.println(">> "+message);
}
@Override
public boolean color() {
return true;
}
});
public Display()
{
core.setOllamaObject(OllamaObject.builder()
.setModel("llama3-AI")
.keep_alive(10)
.addTool(new TimeTool())
.addTool(new PythonRunner())
.stream(false)
.build());
writeLog("Creating base OllamaObject with model: "+core.getOllamaObject().getModel());
System.out.println("Installed tools");
writeLog("Tools installed in this instance");
for(OllamaTool tool : core.getOllamaObject().getTools())
{
if(tool instanceof OllamaFuntionTool funtion)
{
StringBuilder args = new StringBuilder();
OllamaPerameter perameter = funtion.parameters();
if(perameter != null) {
JSONObject obj = perameter.getProperties();
for (String name : obj.keySet()) {
args.append(args.toString().isBlank() ? "" : ", ").append(obj.getJSONObject(name).getString("type")).append(Arrays.stream(perameter.getRequired()).anyMatch(str -> str.equalsIgnoreCase(name)) ? "" : "?").append(" ").append(name);
}
}
System.out.println("> Function: "+funtion.name()+"("+args+")");
writeLog("Function: "+funtion.name()+"("+args+")");
core.addFuntionTool(funtion);
}
}
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Message Trsnscription:");
try {
while (true) {
System.out.print("> ");
StringBuilder message = new StringBuilder(br.readLine());
while (br.ready()) {
message.append("\n").append(br.readLine());
}
if (message.toString().startsWith("/")) {
switch (message.substring(1)) {
case "help":
System.out.print("""
Available commands:
/help Prints this help message.
/bye Exits the program.
/write Flushes the current log stream to file.
""");
break;
case "bye":
writeLog("Exiting program...");
System.out.println("Bye!");
System.exit(0);
return;
case "write":
core.flushLog();
break;
default:
System.out.println("Unknown command: " + message);
}
} else {
writeLog("User: " + message);
core.getOllamaObject().addMessage(new OllamaMessage(OllamaMessageRole.USER, message.toString()));
//System.out.println(ollamaObject.toString());
core.handleResponce(core.qurryOllama());
}
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("Exiting due to exception");
System.exit(-1);
}
}
}

View File

@@ -1,29 +1,25 @@
package me.zacharias.chat;
package me.zacharias.chat.display;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.async.ResultCallbackTemplate;
import com.github.dockerjava.api.command.AttachContainerCmd;
import com.github.dockerjava.api.command.LogContainerCmd;
import com.github.dockerjava.api.command.StartContainerCmd;
import com.github.dockerjava.api.model.Frame;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientBuilder;
import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.core.command.LogContainerResultCallback;
import me.zacharias.chat.ollama.*;
import me.zacharias.chat.ollama.OllamaFunctionArgument;
import me.zacharias.chat.ollama.OllamaFuntionTool;
import me.zacharias.chat.ollama.OllamaPerameter;
import me.zacharias.chat.ollama.OllamaToolRespnce;
import me.zacharias.chat.ollama.exceptions.OllamaToolErrorException;
import org.json.JSONObject;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.logging.Logger;
import static me.zacharias.chat.Main.writeLog;
import static me.zacharias.chat.core.Core.writeLog;
public class PythonRunner extends OllamaFuntionTool {
DockerClient dockerClient;
@@ -172,7 +168,7 @@ public class PythonRunner extends OllamaFuntionTool {
logContainerCmd.exec(new ResultCallback.Adapter<Frame>() {
@Override
public void onNext(Frame item) {
logs.add(item.toString());
logs.add(new String(item.getPayload()).trim());
}
}).awaitCompletion();
} catch (InterruptedException e) {

View File

@@ -1,4 +1,4 @@
package me.zacharias.chat;
package me.zacharias.chat.display;
import me.zacharias.chat.ollama.OllamaFunctionArgument;
import me.zacharias.chat.ollama.OllamaFuntionTool;

View File

@@ -6,21 +6,18 @@ plugins {
group = 'me.zacharias'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
allprojects {
repositories {
mavenCentral()
}
}
dependencies {
implementation("org.json:json:20250107")
implementation("com.github.docker-java:docker-java:3.4.1")
}
subprojects {
apply plugin: 'java'
test {
useJUnitPlatform()
}
jar{
manifest {
attributes 'Main-Class': 'me.zacharias.chat.Main'
dependencies {
implementation("org.json:json:20250107")
implementation("com.github.docker-java:docker-java:3.4.1")
}
}

11
luancher/build.gradle Normal file
View File

@@ -0,0 +1,11 @@
plugins {
id 'java'
}
group = 'me.zacharias'
version = '1.0-SNAPSHOT'
dependencies {
implementation project(":Display")
implementation project(":API")
}

View File

@@ -0,0 +1,55 @@
package me.zacharias.chat.luancher;
import me.zacharias.chat.api.APIServer;
import me.zacharias.chat.display.Display;
public class Launcher {
public static void main(String[] args) {
boolean serverMode = false;
int port = 39075;
String redirectedOutput = null;
for (int i = 0; i < args.length; i++) {
String arg = args[i].toLowerCase();
String argValue = (i + 1 < args.length) ? args[i + 1] : null;
switch (arg) {
case "-s", "--server" -> serverMode = true;
case "-p", "--port" -> {
if (argValue != null) {
port = Integer.parseInt(argValue);
} else {
System.out.println("Missing argument for -p or --port option");
System.exit(1);
}
}
case "-o", "--output" -> {
if (argValue != null) {
redirectedOutput = argValue;
} else {
System.out.println("Missing argument for -o or --output option");
System.exit(1);
}
}
case "--api" -> {
System.out.println("API available at https://server.4zellen.se:3000/Zacharias/chat_thing/wiki/API-Docs");
return;
}
default -> System.out.println("Unknown argument: " + arg);
}
}
if (redirectedOutput != null && !serverMode) {
System.out.println("Cannot run with a redirected output without running in server mode");
return;
}
if (serverMode) {
System.out.println("Starting in API mode...");
new APIServer(port, redirectedOutput);
} else {
System.out.println("Starting in Display mode...");
new Display();
}
}
}

View File

@@ -1,2 +1,3 @@
rootProject.name = 'AI-test'
include 'API', 'Core', 'Display', 'luancher'