Compare commits
17 Commits
a602d30901
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
943c8470a5
|
|||
|
4262dd68c6
|
|||
|
2cf0428d2a
|
|||
|
bbf3c645b0
|
|||
|
4015d37657
|
|||
|
5886d164b4
|
|||
| ab72e1ff78 | |||
|
db53028d1e
|
|||
|
bf5ae6dd3c
|
|||
|
fbf7c504b1
|
|||
|
8426bcac37
|
|||
|
5724a2342d
|
|||
|
95ac78b2d2
|
|||
| 70e7e24aaa | |||
|
57fc7f2533
|
|||
|
840ad468e3
|
|||
|
796271443c
|
22
.gitea/workflows/build.yml
Normal file
22
.gitea/workflows/build.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: arch
|
||||
steps:
|
||||
- run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event."
|
||||
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!"
|
||||
- run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
- run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner."
|
||||
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
|
||||
- name: List files in the repository
|
||||
run: |
|
||||
ls ${{ gitea.workspace }}
|
||||
- run: echo "🍏 This job's status is ${{ job.status }}."
|
||||
3
.idea/gradle.xml
generated
3
.idea/gradle.xml
generated
@@ -12,8 +12,9 @@
|
||||
<option value="$PROJECT_DIR$/API" />
|
||||
<option value="$PROJECT_DIR$/Core" />
|
||||
<option value="$PROJECT_DIR$/Display" />
|
||||
<option value="$PROJECT_DIR$/GeniusAPI" />
|
||||
<option value="$PROJECT_DIR$/MALAPITool" />
|
||||
<option value="$PROJECT_DIR$/Plugin" />
|
||||
<option value="$PROJECT_DIR$/WikipediaTool" />
|
||||
<option value="$PROJECT_DIR$/launcher" />
|
||||
</set>
|
||||
</option>
|
||||
|
||||
8
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
8
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="JavadocReference" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="REPORT_INACCESSIBLE" value="false" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
@@ -14,7 +14,7 @@ dependencies {
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-webflux'
|
||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'
|
||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:3.0.0-M1'
|
||||
//implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = 'me.zacharias'
|
||||
version = '1.0-SNAPSHOT'
|
||||
version = '1.3'
|
||||
|
||||
dependencies {
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package me.zacharias.chat.core;
|
||||
|
||||
import me.zacharias.chat.core.memory.AddMemoryFunction;
|
||||
import me.zacharias.chat.core.memory.GetMemoryFunction;
|
||||
import me.zacharias.chat.core.memory.RemoveMemoryFunction;
|
||||
import me.zacharias.chat.core.memory.*;
|
||||
import me.zacharias.chat.ollama.*;
|
||||
import me.zacharias.chat.ollama.exceptions.OllamaToolErrorException;
|
||||
import me.zacharias.chat.plugin.Plugin;
|
||||
@@ -14,6 +12,7 @@ import org.json.JSONObject;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.time.LocalDateTime;
|
||||
@@ -24,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.
|
||||
*/
|
||||
@@ -53,7 +54,7 @@ public class Core {
|
||||
/**
|
||||
* The IP of the Ollama API.
|
||||
*/
|
||||
private String ollamaIP = "localhost";//"192.168.5.184";
|
||||
private String ollamaIP = "10.0.1.101";//"192.168.5.184";
|
||||
/**
|
||||
* The port of the Ollama API.
|
||||
*/
|
||||
@@ -72,14 +73,18 @@ 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) {
|
||||
if(System.getenv("AI_CHAT_DEBUG") != null) {
|
||||
data = "./data";
|
||||
}
|
||||
else if(System.getProperty("os.name").toLowerCase().contains("windows")) {
|
||||
@@ -87,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";
|
||||
@@ -169,8 +174,14 @@ public class Core {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
scheduler.shutdownNow();
|
||||
try {
|
||||
logWriter.flush();
|
||||
logWriter.close();
|
||||
try {
|
||||
logWriter.flush();
|
||||
logWriter.close();
|
||||
}catch (IOException ignore)
|
||||
{
|
||||
// This exception is kinda expected. Since it can often occur that the logWriter is already closed
|
||||
System.out.println("Failed to flush log file, but that is not a problem.");
|
||||
}
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd_HH-mm-ss");
|
||||
@@ -188,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();
|
||||
@@ -227,6 +238,8 @@ public class Core {
|
||||
addTool(new AddMemoryFunction(), Source.CORE);
|
||||
addTool(new RemoveMemoryFunction(), Source.CORE);
|
||||
addTool(new GetMemoryFunction(), Source.CORE);
|
||||
addTool(new GetMemoriesFunction(), Source.CORE);
|
||||
addTool(new GetMemoryIdentitiesFunction(), Source.CORE);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Ollama object is already set");
|
||||
@@ -254,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -323,8 +336,10 @@ public class Core {
|
||||
|
||||
String ollamaObjectString = ollamaObject.toString();
|
||||
|
||||
ollamaObjectString = ollamaObjectString.replace("\n", "\\n");
|
||||
|
||||
try(DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) {
|
||||
wr.writeBytes(ollamaObjectString);
|
||||
wr.write(ollamaObjectString.getBytes(StandardCharsets.UTF_8));
|
||||
wr.flush();
|
||||
}
|
||||
|
||||
@@ -341,6 +356,17 @@ public class Core {
|
||||
while ((line = reader.readLine()) != null) {
|
||||
response.append(line); // Adds every line to response till the end of file.
|
||||
}
|
||||
}catch (Exception ex)
|
||||
{
|
||||
// If the server returns an error, we read the error stream instead
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getErrorStream()))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error reading error stream: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
@@ -382,34 +408,36 @@ public class Core {
|
||||
if(jsonObject.has("function"))
|
||||
{
|
||||
JSONObject function = jsonObject.getJSONObject("function");
|
||||
List<Pair<OllamaFunctionTool, String>> functions = funtionTools.stream().filter(func -> func.getKey().name().equalsIgnoreCase(function.getString("name"))).toList();
|
||||
List<Pair<OllamaFunctionTool, String>> functions = funtionTools.stream().filter(func -> (func.getKey().name()+func.getValue()).equalsIgnoreCase(function.getString("name"))).toList();
|
||||
|
||||
ArrayList<OllamaFunctionArgument> 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<OllamaFunctionArgument> 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() + (printMessageHandler.color()?"\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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -427,11 +455,12 @@ public class Core {
|
||||
* @param responce the Ollama response
|
||||
*/
|
||||
private void checkIfResponceMessage(JSONObject responce) {
|
||||
if(responce.getJSONObject("message").has("content") && !responce.getJSONObject("message").getString("content").isBlank())
|
||||
String message = responce.getJSONObject("message").getString("content");
|
||||
if(responce.getJSONObject("message").has("content") && !message.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")));
|
||||
printMessageHandler.printMessage((supportColor?"\u001b[32m":"")+(LaunchOptions.getInstance().isShowFullMessage()? message : message.replaceAll("(?s)<think>.*?</think>", "")) +(supportColor?"\u001b[0m":""));
|
||||
writeLog("Response content: "+ message);
|
||||
ollamaObject.addMessage(new OllamaMessage(OllamaMessageRole.ASSISTANT, message));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,6 +470,10 @@ public class Core {
|
||||
*/
|
||||
public static void writeLog(String message)
|
||||
{
|
||||
if(logWriter == null) {
|
||||
System.err.println("!! Log writer is not initialized !!");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd%EEEE HH:mm:ss'#'SSS");
|
||||
@@ -468,8 +501,8 @@ public class Core {
|
||||
{
|
||||
throw new PluginLoadingException("Plugin does not contain a plugin.json file", file.getName());
|
||||
}
|
||||
JSONObject pluginJson = new JSONObject(new String(fs.getPath("/plugin.json").toFile().readAllBytes()));
|
||||
Plugin plugin = loader.loadPlugin(pluginJson, fs);
|
||||
//JSONObject pluginJson = new JSONObject(new String(fs.getPath("/plugin.json").toFile().readAllBytes()));
|
||||
//Plugin plugin = loader.loadPlugin(pluginJson, fs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -15,11 +15,14 @@ public class LaunchOptions {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private LaunchOptions() {}
|
||||
|
||||
private boolean loadOld = true;
|
||||
private boolean autoAccept;
|
||||
private boolean serverMode;
|
||||
private boolean serverCredentialsEnabled;
|
||||
private boolean notDisplay = true;
|
||||
private boolean showFullMessage = false;
|
||||
private int port = 39075;
|
||||
private String redirectOutput;
|
||||
private String serverCredentials;
|
||||
@@ -160,4 +163,20 @@ public class LaunchOptions {
|
||||
public void setNotDisplay(boolean notDisplay) {
|
||||
this.notDisplay = notDisplay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if the full message should be shown in the display.
|
||||
* @return a boolean indicating if the full message should be shown.
|
||||
*/
|
||||
public boolean isShowFullMessage() {
|
||||
return showFullMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the full message should be shown in the display.
|
||||
* @param showFullMessage a boolean indicating if the full message should be shown.
|
||||
*/
|
||||
public void setShowFullMessage(boolean showFullMessage) {
|
||||
this.showFullMessage = showFullMessage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import me.zacharias.chat.core.Core;
|
||||
import org.intellij.lang.annotations.MagicConstant;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
@@ -18,19 +20,25 @@ public class FileHandler {
|
||||
/**
|
||||
* The directory used as base for this instance of {@link FileHandler}. This is where all files that can be read or writen will be located
|
||||
*/
|
||||
private final File directory;
|
||||
private final File/*System*/ directory;
|
||||
|
||||
/**
|
||||
* Creates a new instance as well as setting the {@link #instance} to this new one
|
||||
* @param baseDirectory the directory to be used as base directory
|
||||
*/
|
||||
public FileHandler(@MagicConstant(valuesFromClass = FileHandlerLocation.class) String baseDirectory) {
|
||||
try {
|
||||
FileSystem fs = FileSystems.newFileSystem(new File(baseDirectory).toPath());
|
||||
//fs.getPath()
|
||||
directory = new File(baseDirectory);
|
||||
if (!directory.exists())
|
||||
directory.mkdirs();
|
||||
|
||||
directory = new File(baseDirectory);
|
||||
if(!directory.exists())
|
||||
directory.mkdirs();
|
||||
|
||||
instance = this;
|
||||
instance = this;
|
||||
}catch (Exception ex)
|
||||
{
|
||||
throw new FileHandlerException("Failed to create FileHandler instance with base directory \"" + baseDirectory + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,7 @@ public class AddMemoryFunction extends OllamaFunctionTool {
|
||||
public OllamaPerameter parameters() {
|
||||
return OllamaPerameter.builder()
|
||||
.addProperty("memory", OllamaPerameter.OllamaPerameterBuilder.Type.STRING, "The memory to remember", true)
|
||||
.addProperty("identity", OllamaPerameter.OllamaPerameterBuilder.Type.STRING, "The identity of the memory to remember", true)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -38,8 +39,25 @@ public class AddMemoryFunction extends OllamaFunctionTool {
|
||||
if (args.length == 0) {
|
||||
throw new OllamaToolErrorException(name(), "Missing memory argument");
|
||||
}
|
||||
String value = (String) args[0].value();
|
||||
memory.addMemory(value);
|
||||
return new OllamaToolRespnce(name(), "Added "+value+" to the memory");
|
||||
|
||||
String memory = null;
|
||||
String identity = null;
|
||||
|
||||
for(OllamaFunctionArgument arg : args) {
|
||||
if (arg.argument().equals("memory")) {
|
||||
memory = (String) arg.value();
|
||||
} else if (arg.argument().equals("identity")) {
|
||||
identity = (String) arg.value();
|
||||
} else {
|
||||
throw new OllamaToolErrorException(name(), "Unknown argument: " + arg.argument());
|
||||
}
|
||||
}
|
||||
|
||||
if (memory == null || identity == null) {
|
||||
throw new OllamaToolErrorException(name(), "Missing memory or identity argument");
|
||||
}
|
||||
|
||||
this.memory.addMemory(identity, memory);
|
||||
return new OllamaToolRespnce(name(), "Added "+identity+" to the memory");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.zacharias.chat.core.memory;
|
||||
|
||||
import me.zacharias.chat.core.Core;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
@@ -14,7 +15,7 @@ public class CoreMemory {
|
||||
/**
|
||||
* The singleton instance of CoreMemory.
|
||||
*/
|
||||
private static CoreMemory instance = new CoreMemory("./cache/CoreMemory.json");
|
||||
private static final CoreMemory instance = new CoreMemory(Core.DATA + "/CoreMemory.json");
|
||||
|
||||
/**
|
||||
* Gets the singleton instance of CoreMemory.
|
||||
@@ -38,16 +39,7 @@ public class CoreMemory {
|
||||
while ((buffer = br.readLine()) != null) {
|
||||
data.append(buffer);
|
||||
}
|
||||
JSONArray jsonArray = new JSONArray(data.toString());
|
||||
for(Object obj : jsonArray) {
|
||||
if(obj instanceof String str) {
|
||||
memory.add(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
memory.add(obj.toString());
|
||||
}
|
||||
}
|
||||
memory = new JSONObject(data.toString());
|
||||
}catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -63,18 +55,14 @@ public class CoreMemory {
|
||||
f.delete();
|
||||
}
|
||||
f.createNewFile();
|
||||
|
||||
BufferedWriter bw = new BufferedWriter(new FileWriter(f));
|
||||
JSONArray jsonArray = new JSONArray();
|
||||
|
||||
for(String str : memory) {
|
||||
jsonArray.put(str);
|
||||
}
|
||||
|
||||
bw.write(jsonArray.toString());
|
||||
bw.write(memory.toString());
|
||||
|
||||
bw.close();
|
||||
}catch (Exception e) {
|
||||
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -83,7 +71,7 @@ public class CoreMemory {
|
||||
/**
|
||||
* The memory.
|
||||
*/
|
||||
private ArrayList<String> memory = new ArrayList<>();
|
||||
private JSONObject memory = new JSONObject();
|
||||
/**
|
||||
* The file to store the memory in.
|
||||
*/
|
||||
@@ -91,25 +79,50 @@ public class CoreMemory {
|
||||
|
||||
/**
|
||||
* Gets the memory.
|
||||
* @return The memory
|
||||
* @return A list of memory identifies/names
|
||||
*/
|
||||
public ArrayList<String> getMemory() {
|
||||
return memory;
|
||||
public String[] getMemoriesIdentity() {
|
||||
return memory.keySet().toArray(new String[0]);
|
||||
}
|
||||
|
||||
public String getMemory(String name) {
|
||||
return memory.optString(name, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the memory.
|
||||
* @param name The name/identity of the memory
|
||||
* @param memory The memory
|
||||
*/
|
||||
public void addMemory(String memory) {
|
||||
this.memory.add(memory);
|
||||
public void addMemory(String name, String memory) {
|
||||
this.memory.put(name, memory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the memory.
|
||||
* @param memory The memory to remove
|
||||
* @param name The memory to remove
|
||||
*/
|
||||
public void removeMemory(String memory) {
|
||||
this.memory.remove(memory);
|
||||
public void removeMemory(String name) {
|
||||
this.memory.remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all memories as a JSON string.
|
||||
* @return A JSON string of all memories
|
||||
*/
|
||||
public String getMemories() {
|
||||
ArrayList<String> memories = new ArrayList<>();
|
||||
for (String key : memory.keySet()) {
|
||||
memories.add(key + ": " + 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));
|
||||
}
|
||||
return memories;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package me.zacharias.chat.core.memory;
|
||||
|
||||
import me.zacharias.chat.ollama.OllamaFunctionArgument;
|
||||
import me.zacharias.chat.ollama.OllamaFunctionTool;
|
||||
import me.zacharias.chat.ollama.OllamaPerameter;
|
||||
import me.zacharias.chat.ollama.OllamaToolRespnce;
|
||||
|
||||
public class GetMemoriesFunction extends OllamaFunctionTool {
|
||||
@Override
|
||||
public String name() {
|
||||
return "get_memories";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Retrieves all the memories.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public OllamaPerameter parameters() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OllamaToolRespnce function(OllamaFunctionArgument... args) {
|
||||
return new OllamaToolRespnce(name(), CoreMemory.getInstance().getMemories());
|
||||
}
|
||||
}
|
||||
@@ -24,16 +24,18 @@ public class GetMemoryFunction extends OllamaFunctionTool {
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Retrieves all the memory's";
|
||||
return "Retrieves the memory for a given identity.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public OllamaPerameter parameters() {
|
||||
return null;
|
||||
return OllamaPerameter.builder()
|
||||
.addProperty("identity", OllamaPerameter.OllamaPerameterBuilder.Type.STRING, "The identity of the memory to retrieve", true)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OllamaToolRespnce function(OllamaFunctionArgument... args) {
|
||||
return new OllamaToolRespnce(name(), new JSONObject().put("memory_items", new JSONArray(memory.getMemory())).toString());
|
||||
return new OllamaToolRespnce(name(), memory.getMemory((String) (args[0].value())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package me.zacharias.chat.core.memory;
|
||||
|
||||
import me.zacharias.chat.ollama.OllamaFunctionArgument;
|
||||
import me.zacharias.chat.ollama.OllamaFunctionTool;
|
||||
import me.zacharias.chat.ollama.OllamaPerameter;
|
||||
import me.zacharias.chat.ollama.OllamaToolRespnce;
|
||||
import org.json.JSONArray;
|
||||
|
||||
public class GetMemoryIdentitiesFunction extends OllamaFunctionTool {
|
||||
CoreMemory memory = CoreMemory.getInstance();
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "get_memory_identities";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Retrieves all the memory identities.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public OllamaPerameter parameters() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OllamaToolRespnce function(OllamaFunctionArgument... args) {
|
||||
return new OllamaToolRespnce(this.name(), new JSONArray(memory.getMemoriesIdentity()).toString());
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ public class RemoveMemoryFunction extends OllamaFunctionTool {
|
||||
@Override
|
||||
public OllamaPerameter parameters() {
|
||||
return OllamaPerameter.builder()
|
||||
.addProperty("memory", OllamaPerameter.OllamaPerameterBuilder.Type.STRING, "The memory to forget", true)
|
||||
.addProperty("identity", OllamaPerameter.OllamaPerameterBuilder.Type.STRING, "The identity of the memory to forget", true)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -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<OllamaFunctionArgument> arguments) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (OllamaFunctionArgument argument : arguments) {
|
||||
sb.append(deconstructOllamaFunctionArgument(argument)).append(", ");
|
||||
}
|
||||
return sb.toString().replaceAll(", $", "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())));
|
||||
|
||||
@@ -29,7 +29,7 @@ public class OllamaMessage {
|
||||
public String toString() {
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("role", role.getRole());
|
||||
json.put("content", content);
|
||||
json.put("content", content.replace("\n", "\\n"));
|
||||
return json.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<OllamaTool> tools;
|
||||
ArrayList<Pair<OllamaTool, String>> 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<OllamaMessage> messages, ArrayList<OllamaTool> tools, JSONObject format, Map<String, Object> options, boolean stream, String keep_alive) {
|
||||
private OllamaObject(String model, ArrayList<OllamaMessage> messages, ArrayList<Pair<OllamaTool, String>> tools, JSONObject format, Map<String, Object> 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<OllamaTool> getTools() {
|
||||
public ArrayList<Pair<OllamaTool, String>> 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<? extends Object> 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<OllamaTool, String> 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();
|
||||
@@ -232,7 +262,7 @@ public class OllamaObject {
|
||||
/**
|
||||
* The tools of the Ollama Object.
|
||||
*/
|
||||
ArrayList<OllamaTool> tools = new ArrayList<>();
|
||||
ArrayList<Pair<OllamaTool, String>> 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<? extends OllamaTool> tools) {
|
||||
this.tools.addAll(tools);
|
||||
public OllamaObjectBuilder addToolsExternal(ArrayList<? extends 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#addToolsExternal(ArrayList)}} to add tools from an external source
|
||||
* @param tools The tools to add
|
||||
* @return The {@link OllamaObjectBuilder}
|
||||
*/
|
||||
public OllamaObjectBuilder addTools(ArrayList<Pair<? extends OllamaTool, String>> tools) {
|
||||
for(Pair<? extends OllamaTool, String> 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<OllamaTool, String>... tools) {
|
||||
this.tools.addAll(List.of(tools));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package me.zacharias.chat.ollama.utils;
|
||||
|
||||
import me.zacharias.chat.ollama.OllamaMessage;
|
||||
import me.zacharias.chat.ollama.OllamaMessageRole;
|
||||
|
||||
public class SystemMessage extends OllamaMessage {
|
||||
/**
|
||||
* Creates a new instance of OllamaMessage.
|
||||
*
|
||||
* @param systemMessage The content of the message
|
||||
*/
|
||||
public SystemMessage(String systemMessage) {
|
||||
super(OllamaMessageRole.SYSTEM, systemMessage);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,12 @@ version = '1.0-SNAPSHOT'
|
||||
dependencies {
|
||||
implementation project(":Core")
|
||||
implementation project(":MALAPITool")
|
||||
implementation project(":GeniusAPI")
|
||||
implementation project(":API")
|
||||
implementation project(":WikipediaTool")
|
||||
|
||||
implementation("com.github.docker-java:docker-java-core:3.6.0")
|
||||
implementation("com.github.docker-java:docker-java-transport-httpclient5:3.6.0")
|
||||
}
|
||||
|
||||
test {
|
||||
|
||||
@@ -8,6 +8,9 @@ import me.zacharias.chat.core.files.FileHandlerLocation;
|
||||
import me.zacharias.chat.core.memory.CoreMemory;
|
||||
import me.zacharias.chat.mal.api.MALAPITool;
|
||||
import me.zacharias.chat.ollama.*;
|
||||
import me.zacharias.chat.ollama.utils.SystemMessage;
|
||||
import me.zacharias.neuro.dock.genius.GeniusTools;
|
||||
import me.zacharias.neuro.dock.wikipedia.WikipediaTool;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.*;
|
||||
@@ -51,13 +54,17 @@ public class Display {
|
||||
.keep_alive(10)
|
||||
//.stream(false)
|
||||
//.addFileTools(FileHandlerLocation.DATA_FILES)
|
||||
.addMessages(new SystemMessage("You need to emulate feelings and emotions."))
|
||||
.build());
|
||||
|
||||
core.enablePlugins(Core.PLUGIN_DIRECTORY);
|
||||
|
||||
core.addTool(new TimeTool(), Core.Source.INTERNAL);
|
||||
// TODO: Well Docker failes when luanched.... Fuck
|
||||
//core.addTool(new PythonRunner(core), Core.Source.INTERNAL);
|
||||
core.addTools(new MALAPITool().getOllamaTools());
|
||||
core.addTools(new GeniusTools().getGeniusTools());
|
||||
core.addTools(new WikipediaTool().getWikipediaToolsInstance());
|
||||
|
||||
APIApplication.start();
|
||||
|
||||
@@ -98,9 +105,13 @@ public class Display {
|
||||
case "help":
|
||||
System.out.print("""
|
||||
Available commands:
|
||||
/help Prints this help message.
|
||||
/bye Exits the program.
|
||||
/write Flushes the current log stream to file.
|
||||
/help Prints this help message.
|
||||
/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.
|
||||
""");
|
||||
break;
|
||||
case "bye":
|
||||
@@ -114,7 +125,7 @@ public class Display {
|
||||
case "peek":
|
||||
CoreMemory coreMemory = CoreMemory.getInstance();
|
||||
StringBuilder buffer = new StringBuilder("[");
|
||||
ArrayList<String> memory = new ArrayList<>(coreMemory.getMemory());
|
||||
ArrayList<String> memory = new ArrayList<>(coreMemory.getMemoriesArray());
|
||||
for(int i = 0; i < memory.size(); i++) {
|
||||
String mem = memory.get(i);
|
||||
buffer.append("\"").append(mem).append("\"");
|
||||
@@ -126,6 +137,37 @@ public class Display {
|
||||
writeLog("Memory peek: "+buffer.toString());
|
||||
System.out.println(buffer.toString());
|
||||
break;
|
||||
case "list":
|
||||
writeLog("Tools installed in this instance");
|
||||
|
||||
for(Pair<OllamaFunctionTool, String> funtion : core.getFuntionTools()) {
|
||||
StringBuilder args = new StringBuilder();
|
||||
OllamaPerameter perameter = funtion.getKey().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.getKey().name() + "(" + args + ") [" + funtion.getValue() + "]");
|
||||
writeLog("Function: " + funtion.getKey().name() + "(" + args + ") [" + funtion.getValue() + "]");
|
||||
}
|
||||
break;
|
||||
case "corelist":
|
||||
writeLog("Tools installed in this instance acording to the coire OllamaObject");
|
||||
|
||||
for(Pair<OllamaTool, String> 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" +
|
||||
" DateFiles: " + FileHandlerLocation.DATA_FILES + "\n" +
|
||||
" Plugins: " + Core.PLUGIN_DIRECTORY.getAbsolutePath());
|
||||
break;
|
||||
default:
|
||||
System.out.println("Unknown command: " + message);
|
||||
}
|
||||
|
||||
@@ -8,19 +8,23 @@ import com.github.dockerjava.api.model.BuildResponseItem;
|
||||
import com.github.dockerjava.api.model.Frame;
|
||||
import com.github.dockerjava.api.model.Statistics;
|
||||
import com.github.dockerjava.core.DefaultDockerClientConfig;
|
||||
import com.github.dockerjava.core.DockerClientBuilder;
|
||||
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
|
||||
import com.github.dockerjava.transport.DockerHttpClient;
|
||||
import me.zacharias.chat.core.Core;
|
||||
import me.zacharias.chat.core.Pair;
|
||||
import me.zacharias.chat.ollama.*;
|
||||
import me.zacharias.chat.ollama.exceptions.OllamaToolErrorException;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -112,13 +116,48 @@ public class PythonRunner extends OllamaFunctionTool {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
DefaultDockerClientConfig.Builder config
|
||||
DefaultDockerClientConfig config
|
||||
= DefaultDockerClientConfig.createDefaultConfigBuilder()
|
||||
.withDockerHost("tcp://localhost:2375")
|
||||
.withDockerTlsVerify(false);
|
||||
dockerClient = DockerClientBuilder
|
||||
.getInstance(config)
|
||||
.withDockerTlsVerify(false)
|
||||
.withDockerCertPath("~/.docker")
|
||||
.build();
|
||||
DockerHttpClient dockerHttpClient = new ApacheDockerHttpClient.Builder()
|
||||
.dockerHost(config.getDockerHost())
|
||||
.maxConnections(10)
|
||||
.connectionTimeout(Duration.ofSeconds(100))
|
||||
.responseTimeout(Duration.ofSeconds(100))
|
||||
.sslConfig(config.getSSLConfig())
|
||||
.build();
|
||||
|
||||
DockerHttpClient.Request ping = DockerHttpClient.Request.builder()
|
||||
.method(DockerHttpClient.Request.Method.GET)
|
||||
.path("/_ping")
|
||||
.build();
|
||||
|
||||
try(DockerHttpClient.Response response = dockerHttpClient.execute(ping))
|
||||
{
|
||||
if(!(response.getStatusCode() == 200))
|
||||
{
|
||||
writeLog("Failed to ping docker");
|
||||
System.out.println("Failed to ping docker. Docker components is disabled.");
|
||||
dockerClient = null;
|
||||
return;
|
||||
}
|
||||
if(!(IOUtils.toString(response.getBody(), Charset.defaultCharset()).equals("OK")))
|
||||
{
|
||||
writeLog("Failed to ping docker");
|
||||
System.out.println("Failed to ping docker. Docker components is disabled.");
|
||||
dockerClient = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
writeLog("Failed to ping docker");
|
||||
System.out.println("Failed to ping docker. Docker components is disabled.");
|
||||
dockerClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -142,6 +181,10 @@ public class PythonRunner extends OllamaFunctionTool {
|
||||
|
||||
@Override
|
||||
public OllamaToolRespnce function(OllamaFunctionArgument... args) {
|
||||
if(dockerClient == null)
|
||||
{
|
||||
return new OllamaToolRespnce(name(), "Docker is disabled");
|
||||
}
|
||||
if(args.length == 0)
|
||||
{
|
||||
throw new OllamaToolErrorException(name(), "Missing code argument");
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.hc.client5.http.ssl;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
|
||||
import org.apache.hc.core5.annotation.Contract;
|
||||
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
||||
import org.apache.hc.core5.function.Factory;
|
||||
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
|
||||
import org.apache.hc.core5.reactor.ssl.SSLBufferMode;
|
||||
import org.apache.hc.core5.reactor.ssl.TlsDetails;
|
||||
import org.apache.hc.core5.ssl.SSLContexts;
|
||||
|
||||
/**
|
||||
* TLS upgrade strategy for non-blocking client connections.
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@Contract(threading = ThreadingBehavior.STATELESS)
|
||||
public class DefaultClientTlsStrategy extends AbstractClientTlsStrategy {
|
||||
|
||||
/**
|
||||
* @since 5.4
|
||||
*/
|
||||
public static DefaultClientTlsStrategy createDefault() {
|
||||
return new DefaultClientTlsStrategy(
|
||||
SSLContexts.createDefault(),
|
||||
HostnameVerificationPolicy.BOTH,
|
||||
HttpsSupport.getDefaultHostnameVerifier());
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.4
|
||||
*/
|
||||
public static DefaultClientTlsStrategy createSystemDefault() {
|
||||
return new DefaultClientTlsStrategy(
|
||||
SSLContexts.createSystemDefault(),
|
||||
HttpsSupport.getSystemProtocols(),
|
||||
HttpsSupport.getSystemCipherSuits(),
|
||||
SSLBufferMode.STATIC,
|
||||
HostnameVerificationPolicy.BOTH,
|
||||
HttpsSupport.getDefaultHostnameVerifier());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #createDefault()}.
|
||||
*/
|
||||
@Deprecated
|
||||
public static TlsStrategy getDefault() {
|
||||
return createDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #createSystemDefault()}.
|
||||
*/
|
||||
@Deprecated
|
||||
public static TlsStrategy getSystemDefault() {
|
||||
return createSystemDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated To be removed.
|
||||
*/
|
||||
@Deprecated
|
||||
private Factory<SSLEngine, TlsDetails> tlsDetailsFactory;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link DefaultClientTlsStrategy#DefaultClientTlsStrategy(SSLContext, String[], String[], SSLBufferMode, HostnameVerifier)}
|
||||
*/
|
||||
@Deprecated
|
||||
public DefaultClientTlsStrategy(
|
||||
final SSLContext sslContext,
|
||||
final String[] supportedProtocols,
|
||||
final String[] supportedCipherSuites,
|
||||
final SSLBufferMode sslBufferManagement,
|
||||
final HostnameVerifier hostnameVerifier,
|
||||
final Factory<SSLEngine, TlsDetails> tlsDetailsFactory) {
|
||||
super(sslContext, supportedProtocols, supportedCipherSuites, sslBufferManagement, HostnameVerificationPolicy.CLIENT, hostnameVerifier);
|
||||
this.tlsDetailsFactory = tlsDetailsFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.4
|
||||
*/
|
||||
public DefaultClientTlsStrategy(
|
||||
final SSLContext sslContext,
|
||||
final String[] supportedProtocols,
|
||||
final String[] supportedCipherSuites,
|
||||
final SSLBufferMode sslBufferManagement,
|
||||
final HostnameVerificationPolicy hostnameVerificationPolicy,
|
||||
final HostnameVerifier hostnameVerifier) {
|
||||
super(sslContext, supportedProtocols, supportedCipherSuites, sslBufferManagement, hostnameVerificationPolicy, hostnameVerifier);
|
||||
}
|
||||
|
||||
public DefaultClientTlsStrategy(
|
||||
final SSLContext sslContext,
|
||||
final String[] supportedProtocols,
|
||||
final String[] supportedCipherSuites,
|
||||
final SSLBufferMode sslBufferManagement,
|
||||
final HostnameVerifier hostnameVerifier) {
|
||||
this(sslContext, supportedProtocols, supportedCipherSuites, sslBufferManagement, HostnameVerificationPolicy.CLIENT, hostnameVerifier);
|
||||
}
|
||||
|
||||
public DefaultClientTlsStrategy(
|
||||
final SSLContext sslContext,
|
||||
final HostnameVerifier hostnameVerifier) {
|
||||
this(sslContext, null, null, SSLBufferMode.STATIC, hostnameVerifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.4
|
||||
*/
|
||||
public DefaultClientTlsStrategy(
|
||||
final SSLContext sslContext,
|
||||
final HostnameVerificationPolicy hostnameVerificationPolicy,
|
||||
final HostnameVerifier hostnameVerifier) {
|
||||
this(sslContext, null, null, SSLBufferMode.STATIC, hostnameVerificationPolicy, hostnameVerifier);
|
||||
}
|
||||
|
||||
public DefaultClientTlsStrategy(final SSLContext sslContext) {
|
||||
this(sslContext, HttpsSupport.getDefaultHostnameVerifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
void applyParameters(final SSLEngine sslEngine, final SSLParameters sslParameters, final String[] appProtocols) {
|
||||
sslParameters.setApplicationProtocols(appProtocols);
|
||||
sslEngine.setSSLParameters(sslParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecated")
|
||||
TlsDetails createTlsDetails(final SSLEngine sslEngine) {
|
||||
return tlsDetailsFactory != null ? tlsDetailsFactory.create(sslEngine) : null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.apache.hc.client5.http.ssl;
|
||||
|
||||
/**
|
||||
* Hostname verification policy.
|
||||
*
|
||||
* @see javax.net.ssl.HostnameVerifier
|
||||
* @see DefaultHostnameVerifier
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
public enum HostnameVerificationPolicy {
|
||||
|
||||
/**
|
||||
* Hostname verification is delegated to the JSSE provider, usually executed during the TLS handshake.
|
||||
*/
|
||||
BUILTIN,
|
||||
/**
|
||||
* Hostname verification is executed by HttpClient post TLS handshake.
|
||||
*/
|
||||
CLIENT,
|
||||
/**
|
||||
* Hostname verification is executed by the JSSE provider and by HttpClient post TLS handshake.
|
||||
*/
|
||||
BOTH
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.apache.hc.client5.http.ssl;
|
||||
|
||||
import org.apache.hc.core5.annotation.Contract;
|
||||
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
||||
import org.apache.hc.core5.http.protocol.HttpContext;
|
||||
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
|
||||
@Contract(threading = ThreadingBehavior.STATELESS)
|
||||
public interface TlsSocketStrategy {
|
||||
|
||||
/**
|
||||
* Upgrades the given plain socket and executes the TLS handshake over it.
|
||||
*
|
||||
* @param socket the existing plain socket
|
||||
* @param target the name of the target host.
|
||||
* @param port the port to connect to on the target host.
|
||||
* @param context the actual HTTP context.
|
||||
* @param attachment connect request attachment.
|
||||
* @return socket upgraded to TLS.
|
||||
*/
|
||||
SSLSocket upgrade(
|
||||
Socket socket,
|
||||
String target,
|
||||
int port,
|
||||
Object attachment,
|
||||
HttpContext context) throws IOException;
|
||||
|
||||
}
|
||||
25
GeniusAPI/README.md
Normal file
25
GeniusAPI/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Genius API
|
||||
|
||||
## Important notes
|
||||
### Legal Notice
|
||||
|
||||
This tool is provided for educational and experimental purposes only.
|
||||
|
||||
Please be aware that the Genius API Terms of Service **prohibit** web scraping of their website. This project contains code that performs scraping to retrieve song lyrics directly from Genius.com, which may violate those terms.
|
||||
|
||||
Since Genius does not provide official API endpoints for lyrics due to copyright restrictions, this tool includes scraping functionality as a workaround. However, scraping may result in legal or technical consequences such as IP bans, rate limiting, or other restrictions imposed by Genius.
|
||||
|
||||
**Use this tool responsibly and at your own risk.** The author does not encourage or endorse violating any third-party terms of service or applicable laws and disclaims any liability arising from misuse.
|
||||
|
||||
Whenever possible, prefer using official API endpoints and respect copyright laws.
|
||||
|
||||
## API key required
|
||||
This tool requires a Genius API key to work since it fetches song metadata from the Genius API.
|
||||
|
||||
The API key shuld be stored in `${DATA_DIR}/geniusapi.json` using the following format:
|
||||
```json
|
||||
{
|
||||
"client_id": "<your_client_id>",
|
||||
"client_secret": "<your_client_secret>"
|
||||
}
|
||||
```
|
||||
24
GeniusAPI/build.gradle
Normal file
24
GeniusAPI/build.gradle
Normal file
@@ -0,0 +1,24 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
group = 'me.zacharias'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation platform('org.junit:junit-bom:5.10.0')
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
|
||||
implementation "org.jsoup:jsoup:1.20.1"
|
||||
implementation 'io.github.classgraph:classgraph:4.8.158'
|
||||
|
||||
implementation project(":Core")
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package me.zacharias.neuro.dock.genius;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface GeniusEndpoint {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package me.zacharias.neuro.dock.genius;
|
||||
|
||||
import me.zacharias.chat.ollama.OllamaFunctionTool;
|
||||
|
||||
public abstract class GeniusEndpointTool extends OllamaFunctionTool {
|
||||
protected GeniusTools geniusToolsInstance;
|
||||
|
||||
public GeniusEndpointTool(GeniusTools geniusTools) {
|
||||
super();
|
||||
this.geniusToolsInstance = geniusTools;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package me.zacharias.neuro.dock.genius;
|
||||
|
||||
import io.github.classgraph.ClassGraph;
|
||||
import io.github.classgraph.ClassInfo;
|
||||
import io.github.classgraph.ClassInfoList;
|
||||
import io.github.classgraph.ScanResult;
|
||||
import me.zacharias.chat.core.Core;
|
||||
import me.zacharias.chat.ollama.OllamaFunctionTool;
|
||||
import me.zacharias.chat.ollama.OllamaFunctionTools;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
|
||||
public class GeniusTools {
|
||||
|
||||
public final String Client_ID;
|
||||
public final String Client_Secret;
|
||||
public final String Access_Token;
|
||||
public final String BaseURL = "https://api.genius.com";
|
||||
public final OllamaFunctionTools GeniusTools;
|
||||
public final File CacheFile = new File(Core.DATA_DIR + "/genius_cache.json");
|
||||
public JSONObject CacheData;
|
||||
|
||||
public GeniusTools() {
|
||||
super();
|
||||
try {
|
||||
JSONObject obj = new JSONObject(Files.readString(Path.of(Core.DATA_DIR + "/geniusapi.json")));
|
||||
this.Client_ID = obj.getString("client_id");
|
||||
this.Client_Secret = obj.getString("client_secret");
|
||||
this.Access_Token = obj.getString("access_token");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if(CacheFile.exists()) {
|
||||
try{
|
||||
CacheData = new JSONObject(Files.readString(CacheFile.toPath()));
|
||||
}catch (IOException ex)
|
||||
{
|
||||
ex.printStackTrace();
|
||||
if(CacheData == null)
|
||||
CacheData = new JSONObject();
|
||||
CacheFile.delete();
|
||||
}
|
||||
}
|
||||
else {
|
||||
CacheData = new JSONObject();
|
||||
}
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
try {
|
||||
Files.writeString(CacheFile.toPath(), CacheData.toString());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}));
|
||||
|
||||
OllamaFunctionTools.OllamaFunctionToolsBuilder builder = new OllamaFunctionTools.OllamaFunctionToolsBuilder();
|
||||
|
||||
try(ScanResult scanResult = new ClassGraph().enableAllInfo().acceptPackages("me.zacharias.neuro.dock.genius.endpoints").scan()) {
|
||||
ClassInfoList endpoints = scanResult.getClassesWithAnnotation(GeniusEndpoint.class.getName());
|
||||
for (ClassInfo classInfo : endpoints) {
|
||||
Class<?> clazz = classInfo.loadClass();
|
||||
if (OllamaFunctionTool.class.isAssignableFrom(clazz)) {
|
||||
//System.out.println("Found endpoint: " + clazz.getName() + " With constructor: " + clazz.getDeclaredConstructors().f.getName() + " and arguments: " + Arrays.toString(clazz.getDeclaredConstructor().getParameterTypes()));
|
||||
GeniusEndpointTool tool = (GeniusEndpointTool) clazz.getDeclaredConstructor(GeniusTools.class).newInstance(this);
|
||||
builder.addTool(tool, Core.Source.INTERNAL);
|
||||
}
|
||||
}
|
||||
} catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
this.GeniusTools = builder.build();
|
||||
}
|
||||
|
||||
public OllamaFunctionTools getGeniusTools() {
|
||||
return this.GeniusTools;
|
||||
}
|
||||
|
||||
public JSONObject getGeniusEndpoint(String endpoint, Map<String, String> params) {
|
||||
try {
|
||||
URL url = new URL(this.BaseURL + endpoint + "?" + ParameterStringBuilder.getParamsString(params));
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("GET");
|
||||
conn.setRequestProperty("Authorization", "Bearer " + this.Access_Token);
|
||||
conn.setRequestProperty("Accept", "application/json");
|
||||
|
||||
if (conn.getResponseCode() != 200) {
|
||||
throw new IOException("Failed to connect to Genius API: " + conn.getResponseCode());
|
||||
}
|
||||
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
String inputLine;
|
||||
StringBuilder response = new StringBuilder();
|
||||
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
response.append(inputLine);
|
||||
}
|
||||
in.close();
|
||||
|
||||
return new JSONObject(response.toString());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String hasCache(int song_id) {
|
||||
if(CacheData.has(Integer.toString(song_id)))
|
||||
return CacheData.getString(Integer.toString(song_id));
|
||||
return null;
|
||||
}
|
||||
|
||||
public void cacheLyrics(int song_id, String lyrics) {
|
||||
CacheData.put(Integer.toString(song_id), lyrics);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package me.zacharias.neuro.dock.genius;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
public class ParameterStringBuilder {
|
||||
public static String getParamsString(Map<String, String> params)
|
||||
throws UnsupportedEncodingException {
|
||||
if(params == null)
|
||||
return "";
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
result.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8));
|
||||
result.append("=");
|
||||
result.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
|
||||
result.append("&");
|
||||
}
|
||||
|
||||
String resultString = result.toString();
|
||||
return !resultString.isEmpty()
|
||||
? resultString.substring(0, resultString.length() - 1)
|
||||
: resultString;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package me.zacharias.neuro.dock.genius.endpoints;
|
||||
|
||||
import me.zacharias.chat.ollama.OllamaFunctionArgument;
|
||||
import me.zacharias.chat.ollama.OllamaPerameter;
|
||||
import me.zacharias.chat.ollama.OllamaToolRespnce;
|
||||
import me.zacharias.chat.ollama.exceptions.OllamaToolErrorException;
|
||||
import me.zacharias.neuro.dock.genius.GeniusEndpoint;
|
||||
import me.zacharias.neuro.dock.genius.GeniusEndpointTool;
|
||||
import me.zacharias.neuro.dock.genius.GeniusTools;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static me.zacharias.chat.ollama.OllamaPerameter.OllamaPerameterBuilder.Type.STRING;
|
||||
|
||||
@GeniusEndpoint
|
||||
public class FindSong extends GeniusEndpointTool {
|
||||
public FindSong(GeniusTools geniusTools) {
|
||||
super(geniusTools);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "findsong";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Finds a song by title from the Genius API.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public OllamaPerameter parameters() {
|
||||
return OllamaPerameter.builder()
|
||||
.addProperty("title", STRING, "The title, artitst, and song_id of the song to find.", true)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OllamaToolRespnce function(OllamaFunctionArgument... args) {
|
||||
String title = (String) args[0].value();
|
||||
if (title == null || title.isEmpty()) {
|
||||
throw new OllamaToolErrorException(this.name(), "Title cannot be null or empty.");
|
||||
}
|
||||
|
||||
JSONObject response = this.geniusToolsInstance.getGeniusEndpoint("/search", Map.of("q", title));
|
||||
|
||||
JSONArray responseData = new JSONArray();
|
||||
|
||||
for(Object obj : response.getJSONObject("response").getJSONArray("hits")) {
|
||||
if(obj instanceof JSONObject songResult) {
|
||||
JSONObject song = songResult.getJSONObject("result");
|
||||
JSONObject songData = new JSONObject();
|
||||
songData.put("title", song.getString("title"));
|
||||
songData.put("artist", song.getString("artist_names"));
|
||||
songData.put("id", song.getInt("id"));
|
||||
responseData.put(songData);
|
||||
}
|
||||
}
|
||||
|
||||
if (responseData.isEmpty()) {
|
||||
throw new OllamaToolErrorException(this.name(), "No songs found for the given title.");
|
||||
}
|
||||
|
||||
return new OllamaToolRespnce(this.name(), responseData.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package me.zacharias.neuro.dock.genius.endpoints;
|
||||
|
||||
import me.zacharias.chat.ollama.OllamaFunctionArgument;
|
||||
import me.zacharias.chat.ollama.OllamaPerameter;
|
||||
import me.zacharias.chat.ollama.OllamaToolRespnce;
|
||||
import me.zacharias.chat.ollama.exceptions.OllamaToolErrorException;
|
||||
import me.zacharias.neuro.dock.genius.GeniusEndpoint;
|
||||
import me.zacharias.neuro.dock.genius.GeniusEndpointTool;
|
||||
import me.zacharias.neuro.dock.genius.GeniusTools;
|
||||
import org.json.JSONObject;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.nodes.Node;
|
||||
import org.jsoup.nodes.TextNode;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static me.zacharias.chat.core.Core.writeLog;
|
||||
|
||||
/**
|
||||
* This class is an Ollama tool that fetches the lyrics of a song by its ID from the Genius API.
|
||||
* It extends the GeniusEndpointTool to utilize the GeniusTools instance for API calls and caching.
|
||||
* <p>
|
||||
* <h1>Legal Notice</h1>
|
||||
* <p>
|
||||
* I (the developer) do NOT own the rights to the lyrics fetched by this tool, nor do I endorse scraping lyrics from Genius.com,
|
||||
* which may violate their Terms of Service.
|
||||
* </p>
|
||||
* <p>
|
||||
* I disclaim any responsibility or liability for how this tool is used.
|
||||
* Use this tool at your own risk, and please respect all applicable copyright laws and third-party terms.
|
||||
* </p>
|
||||
*/
|
||||
@GeniusEndpoint
|
||||
public class GetLyrics extends GeniusEndpointTool {
|
||||
public GetLyrics(GeniusTools geniusTools) {
|
||||
super(geniusTools);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "get_lyrics";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Gets the lyrics of a song by its ID from the Genius API.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public OllamaPerameter parameters() {
|
||||
return OllamaPerameter.builder()
|
||||
.addProperty("song_id", OllamaPerameter.OllamaPerameterBuilder.Type.INT, "The ID of the song to get lyrics for.", true)
|
||||
.build();
|
||||
}
|
||||
|
||||
@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)
|
||||
{
|
||||
return new OllamaToolRespnce(this.name(), lyricsStr.trim());
|
||||
}
|
||||
|
||||
JSONObject obj = geniusToolsInstance.getGeniusEndpoint("/songs/" + args[0].value(), null);
|
||||
|
||||
String lyrics_path = obj.getJSONObject("response").getJSONObject("song").getString("url");
|
||||
try {
|
||||
// WARNING: This request scrapes the lyrics from the Genius website.
|
||||
// Which is against their Terms of Service. Use responsibly
|
||||
Document doc = Jsoup.connect(lyrics_path)
|
||||
.userAgent("insomnia/11.1.0") // TODO: replace with somthing else then insomnia! since we in no way support what ever Insomnia's User-Agent says we do
|
||||
.header("host", "genius.com")
|
||||
.header("accept", "*/*")
|
||||
.get();
|
||||
|
||||
writeLog("Fetching lyrics from: " + lyrics_path);
|
||||
|
||||
Elements containers = doc.select("div[data-lyrics-container=true]");
|
||||
|
||||
StringBuilder lyrics = new StringBuilder();
|
||||
|
||||
for (Element container : containers) {
|
||||
for(Node n : container.childNodes())
|
||||
{
|
||||
if(n instanceof Element e) {
|
||||
if (e.attribute("data-exclude-from-selection") != null && e.attr("data-exclude-from-selection").equals("true")) {
|
||||
continue;
|
||||
}
|
||||
else if(e.tagName().equalsIgnoreCase("br"))
|
||||
{
|
||||
lyrics.append("\n");
|
||||
}
|
||||
else {
|
||||
//System.out.println(container.tagName());
|
||||
String s = e.text();
|
||||
lyrics.append(s.trim());
|
||||
}
|
||||
}
|
||||
else if(n instanceof TextNode tn)
|
||||
{
|
||||
String s = tn.text();
|
||||
if (!s.isBlank()) {
|
||||
lyrics.append(s.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
geniusToolsInstance.cacheLyrics((int) args[0].value(), lyrics.toString());
|
||||
|
||||
return new OllamaToolRespnce(this.name(), lyrics.toString().trim());
|
||||
}catch (Exception ex)
|
||||
{
|
||||
ex.printStackTrace();
|
||||
throw new OllamaToolErrorException(this.name(), "Failed to fetch lyrics.");
|
||||
}
|
||||
}
|
||||
}
|
||||
60
GeniusAPI/src/test/java/LyricsFetch.java
Normal file
60
GeniusAPI/src/test/java/LyricsFetch.java
Normal file
@@ -0,0 +1,60 @@
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.nodes.Node;
|
||||
import org.jsoup.nodes.TextNode;
|
||||
import org.jsoup.select.Elements;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.swing.text.html.HTML;
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static me.zacharias.chat.core.Core.writeLog;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
public class LyricsFetch {
|
||||
Pattern pattern = Pattern.compile("(?i)\\[(verse.*)|(chorus.*)|(bridge.*)|(outro.*)|(intro.*)]");
|
||||
|
||||
@Test
|
||||
public void testFetchLyrics() throws Exception {
|
||||
Document doc = Jsoup.connect("https://genius.com/Neuro-sama-life-lyrics")
|
||||
.userAgent("Mozilla/5.0")
|
||||
.get();
|
||||
|
||||
Elements containers = doc.select("div[data-lyrics-container=true]");
|
||||
|
||||
StringBuilder lyrics = new StringBuilder();
|
||||
|
||||
for (Element container : containers) {
|
||||
for(Node n : container.childNodes())
|
||||
{
|
||||
if(n instanceof Element e) {
|
||||
if (e.attribute("data-exclude-from-selection") != null && e.attr("data-exclude-from-selection").equals("true")) {
|
||||
continue;
|
||||
}
|
||||
else if(e.tagName().equalsIgnoreCase("br"))
|
||||
{
|
||||
lyrics.append("\n");
|
||||
}
|
||||
else {
|
||||
System.out.println(container.tagName());
|
||||
String s = e.text();
|
||||
lyrics.append(s.trim());
|
||||
}
|
||||
}
|
||||
else if(n instanceof TextNode tn)
|
||||
{
|
||||
String s = tn.text();
|
||||
if (!s.isBlank()) {
|
||||
lyrics.append(s.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(lyrics.toString());
|
||||
assertNotNull(lyrics.toString());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import java.util.Map;
|
||||
public class ParameterStringBuilder {
|
||||
public static String getParamsString(Map<String, String> params)
|
||||
throws UnsupportedEncodingException {
|
||||
if(params == null)
|
||||
return "";
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
|
||||
15
README.md
15
README.md
@@ -1,5 +1,6 @@
|
||||
# Chat thing
|
||||
(better name pending probably)
|
||||
# NeuroDock
|
||||
Note:
|
||||
This project is not in colabiration with Neuro-Sama, Vedal987, Alexcra. This is an independent project and name, aswell as use case similarities are cucidental.
|
||||
|
||||
## What is it?
|
||||
Well it's basically a font end for Ollama like [Open-WebUI](https://openwebui.com/), but meant to run a local model and open up a somewhat easy to use API system for tools.
|
||||
@@ -7,7 +8,15 @@ Well it's basically a font end for Ollama like [Open-WebUI](https://openwebui.co
|
||||
## Use cases?
|
||||
While the primary goal is providing a somewhat more modular frontend for Ollama, you can also use this system to integrate AI into your own application, For example AI could act as a player in a game or interact with external system with the use of tools via the API
|
||||
|
||||
simple examples are in the Display module where i gave it the ability to access python through docker and get the current date and time both with the [OllamaFunctionTool](https://server.4zellen.se:3000/Zacharias/chat_thing/src/branch/master/Core/src/main/java/me/zacharias/chat/ollama/OllamaFuntionTool.java) thru my Ollama framework
|
||||
simple examples are in the Display module where i gave it the ability to access python through docker and get the current date and time both with the [OllamaFunctionTool](https://server.4zellen.se:3000/Zacharias/chat_thing/src/branch/master/Core/src/main/java/me/zacharias/chat/ollama/OllamaFunctionTool.java) thru my Ollama framework
|
||||
|
||||
## Legal Notice Regarding Scraping
|
||||
|
||||
A submodule of this project includes functionality that scrapes song lyrics directly from Genius.com, which violates Genius API's Terms of Service.
|
||||
|
||||
The author **does not endorse or encourage** scraping or any other use that violates third-party terms or copyright laws.
|
||||
|
||||
Use this software **at your own risk**. The author disclaims any liability for legal or technical consequences arising from its use.
|
||||
|
||||
## API
|
||||
The documentation for the API is available at the gitea wiki under [API docs](https://server.4zellen.se:3000/Chat_things/NeuroDock/wiki/API-Docs)
|
||||
|
||||
23
WikipediaTool/build.gradle
Normal file
23
WikipediaTool/build.gradle
Normal file
@@ -0,0 +1,23 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
group = 'me.zacharias'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation platform('org.junit:junit-bom:5.10.0')
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
|
||||
implementation("io.github.fastily:jwiki:1.11.0")
|
||||
|
||||
implementation project(":Core")
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package me.zacharias.neuro.dock.wikipedia;
|
||||
|
||||
import io.github.fastily.jwiki.core.Wiki;
|
||||
import me.zacharias.chat.ollama.OllamaFunctionArgument;
|
||||
import me.zacharias.chat.ollama.OllamaFunctionTool;
|
||||
import me.zacharias.chat.ollama.OllamaPerameter;
|
||||
import me.zacharias.chat.ollama.OllamaToolRespnce;
|
||||
|
||||
public class GetWikiPageText extends OllamaFunctionTool {
|
||||
Wiki wiki = WikipediaTool.wiki;
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "get_wiki_page_text";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Gets the text of a Wikipedia page by its title.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public OllamaPerameter parameters() {
|
||||
return OllamaPerameter.builder()
|
||||
.addProperty("title", OllamaPerameter.OllamaPerameterBuilder.Type.STRING, "The title of the Wikipedia page to retrieve text from.", true)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OllamaToolRespnce function(OllamaFunctionArgument... args) {
|
||||
return new OllamaToolRespnce(name(), wiki.getPageText((String) args[0].value()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package me.zacharias.neuro.dock.wikipedia;
|
||||
|
||||
import io.github.fastily.jwiki.core.Wiki;
|
||||
import me.zacharias.chat.core.Core;
|
||||
import me.zacharias.chat.ollama.OllamaFunctionTools;
|
||||
|
||||
public class WikipediaTool {
|
||||
private OllamaFunctionTools wikipediaToolsInstance;
|
||||
|
||||
public static Wiki wiki = new Wiki.Builder()
|
||||
//.withDomain("en.wikipedia.org")
|
||||
.build();
|
||||
|
||||
public WikipediaTool() {
|
||||
this.wikipediaToolsInstance = OllamaFunctionTools.builder()
|
||||
.addTool(new GetWikiPageText(), Core.Source.INTERNAL)
|
||||
.build();
|
||||
}
|
||||
|
||||
public OllamaFunctionTools getWikipediaToolsInstance() {
|
||||
return wikipediaToolsInstance;
|
||||
}
|
||||
}
|
||||
32
build.gradle
32
build.gradle
@@ -7,6 +7,9 @@ group = 'me.zacharias'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
allprojects {
|
||||
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
@@ -19,7 +22,6 @@ subprojects {
|
||||
|
||||
dependencies {
|
||||
implementation("org.json:json:20250107")
|
||||
implementation("com.github.docker-java:docker-java:3.4.1")
|
||||
implementation("org.jetbrains:annotations:23.1.0")
|
||||
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
|
||||
@@ -43,4 +45,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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ public class Launcher {
|
||||
}
|
||||
}
|
||||
case "--api" -> {
|
||||
System.out.println("API available at https://server.4zellen.se:3000/Zacharias/chat_thing/wiki/API-Docs");
|
||||
System.out.println("API available at https://git.server.4zellen.se/Zacharias/chat_thing/wiki/API-Docs");
|
||||
return;
|
||||
}
|
||||
case "--help", "-h" -> {
|
||||
@@ -59,6 +59,7 @@ public class Launcher {
|
||||
-o --output Redirects the API Server output to another file
|
||||
-y Auto accepts to prompts, used for a seamless run. Not recomended when running as Display
|
||||
-d --dontLoadOld Don't load old messages
|
||||
-f --full-message Shows the models <think>...</think> block in the output
|
||||
--api Provides API docs
|
||||
""");
|
||||
return;
|
||||
@@ -71,6 +72,10 @@ public class Launcher {
|
||||
{
|
||||
options.setLoadOld(false);
|
||||
}
|
||||
case "-f", "--full-message" ->
|
||||
{
|
||||
options.setShowFullMessage(true);
|
||||
}
|
||||
|
||||
default -> {
|
||||
System.out.println("Unknown option: " + arg+"\nUse --help for help");
|
||||
|
||||
@@ -3,3 +3,13 @@ include 'API', 'Core', 'Display', 'launcher'
|
||||
|
||||
|
||||
include 'MALAPITool'
|
||||
/**
|
||||
* LEGAL WARNING:
|
||||
* This module contains code that scrapes lyrics from Genius.com, which is against Genius API's Terms of Service.
|
||||
*
|
||||
* If you wish to avoid any potential legal issues, comment out or remove the line below and ensure no modules depend on this.
|
||||
* The core NeuroDock system will still function without this module.
|
||||
*/
|
||||
include 'GeniusAPI'
|
||||
|
||||
include 'WikipediaTool'
|
||||
Reference in New Issue
Block a user