Skip to content

Commit

Permalink
Fixed Logic for argument description not adding required or no null i…
Browse files Browse the repository at this point in the history
…f there is a description already provided, made it so that ai lama won't create a new assistant unless a new model is given to it

WIP Response Formatter
  • Loading branch information
ZeyoYT committed May 7, 2024
1 parent a0f6e76 commit 4d05868
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 64 deletions.
61 changes: 3 additions & 58 deletions src/main/java/me/ailama/commands/AiCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public void handleCommand(SlashCommandInteractionEvent event) {
event.deferReply().queue();
}

AiLama.getInstance().startTimer();

// Set Configurations
String modelOption = event.getOption("model") != null ? event.getOption("model").getAsString() : null;
String queryOption = event.getOption("ask").getAsString();
Expand Down Expand Up @@ -97,66 +99,9 @@ public void handleCommand(SlashCommandInteractionEvent event) {
}
else
{
String tools = String.format("tools = %s",OllamaManager.getInstance().getFinalJson().build());

// generate normal response based on the query if the url option is not provided or the web option is not provided
response = OllamaManager.getInstance().
createAssistantX(modelOption)
.systemMessageProvider(x ->
String.format("""
You are a helpful AI assistant, you have a score, which can either be good or bad,
you need to maintain a good score to be helpful, if you don't maintain a good score then you will be considered unhelpful.
you will try to answer the users need as best as you can and only in JSON format, else you will be given a bad score.
any of the tools description listed below match the specific needs of the query then use the tool to answer the query,
the tools description is as specific as possible, so don't assume that the tool can be used for anything else.
finally if a tool is matched then give response using following schema:
{
"tooled": true,
"name": "tool_name",
"arguments": {
"argument_name": "value",
...
},
"reason": "detailed_reason_for_using_tool"
}
the tool_name is the name of the tool that you are using, the arguments are the arguments that you are passing to the tool.
following are the rules for tools:
if the tool description does not specify the user's needs then don't respond using a tool else you will be given a bad score.
if you don't pass the required arguments to the tool then you will be given a bad score.
if you pass a null value to a argument that specified NOT_NULL in its description then you will be given a bad score.
if you don't respect the arguments data type, you will be given a bad score.
if you don't respect the arguments description, you will be given a bad score.
the reason should not exceed 200 characters, and if it does, you will be given a bad score.
and if you don't follow the schema, you will be given a bad score, but if you follow the schema, you will be given a good score.
if you don't find a tool that match the requirements of the user then respond to the user normally,
and also make the response to be encoded for the JSON format or you will be given a bad score,
and use the following schema:
{
"tooled": false,
"response": [
"paragraph",
"paragraph",
...
]
}
in the above schema, the response is an array of paragraphs that you want to respond to the user, minimum of 1 paragraph.
each new paragraph should be a new string in the array.
between each paragraph, there should be '\\n'.
%s
""",tools)
)
.build()
getTooledAssistant(modelOption)
.answer(queryOption);

ObjectMapper mapper = new ObjectMapper();
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/me/ailama/handler/annotations/ResponseFormatter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package me.ailama.handler.annotations;

public @interface ResponseFormatter {

String[] responseOrder();

String[] responseVariables();

String preFormattedResponse() default "";

boolean isPreFormatted() default false;

// if false, then direct response will be returned
boolean isResponseOrder() default false;
}
2 changes: 2 additions & 0 deletions src/main/java/me/ailama/handler/annotations/Tool.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@
String description();

Args[] arguments();

ResponseFormatter responseFormatter() default @ResponseFormatter(responseOrder = {}, responseVariables = {}, preFormattedResponse = "", isPreFormatted = false, isResponseOrder = false);
}
114 changes: 113 additions & 1 deletion src/main/java/me/ailama/handler/commandhandler/OllamaManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import me.ailama.config.Config;
import me.ailama.handler.JsonBuilder.JsonArray;
import me.ailama.handler.JsonBuilder.JsonObject;
import me.ailama.handler.annotations.ResponseFormatter;
import me.ailama.handler.annotations.Tool;
import me.ailama.handler.interfaces.Assistant;
import me.ailama.main.AiLama;
Expand Down Expand Up @@ -46,6 +47,8 @@ public class OllamaManager {

private final HashMap<Class<?>, ArrayList<String>> classInstanceMap;

private Assistant assistant;

public OllamaManager() {

url = AiLama.getInstance().fixUrl(Config.get("OLLAMA_URL") + ":" + Config.get("OLLAMA_PORT"));
Expand All @@ -54,6 +57,7 @@ public OllamaManager() {

tools = new HashMap<>();
classInstanceMap = new HashMap<>();
assistant = null;

addTool(AiLama.getInstance());
addTool(new MathTools());
Expand All @@ -62,6 +66,9 @@ public OllamaManager() {
addTool(new UtilityTools());
}

/*
Uses Reflection Library to add the tools to the HashMap with specific annotation
*/
public void addTool(Object toolClass) {

ArrayList<String> toolsList = new ArrayList<>();
Expand Down Expand Up @@ -120,7 +127,7 @@ public JsonArray getFinalJson() {
argument.add("name",toolAnnotation.arguments()[i].name())
.add("type",toolAnnotation.arguments()[i].Type());

if(!toolAnnotation.arguments()[i].description().isEmpty()) {
if(toolAnnotation.arguments()[i].required() || toolAnnotation.arguments()[i].noNull()) {

StringBuilder description = getStringBuilder(toolAnnotation, i);

Expand All @@ -132,6 +139,23 @@ public JsonArray getFinalJson() {

arguments.objects(argumentJsonObjects);
object.add("arguments",arguments);

JsonObject responseFormatterObject = new JsonObject();

// add response formatter
ResponseFormatter responseFormatter = toolAnnotation.responseFormatter();

responseFormatterObject.add("response_order",responseFormatter.responseOrder());
responseFormatterObject.add("response_variables",responseFormatter.responseVariables());

if(responseFormatter.isPreFormatted()) {
responseFormatterObject.add("pre_formatted_response",responseFormatter.preFormattedResponse());
}
else {
responseFormatterObject.add("pre_formatted_response","");
}

object.add("response_formatter",responseFormatterObject);
}

// Add the tool object to the array
Expand Down Expand Up @@ -210,6 +234,94 @@ public static List<Method> getMethodsAnnotated(final Class<?> type) {
return methods;
}

public Assistant getTooledAssistant(String modelOption) {

if(assistant != null) {

if(modelOption != null) {
assistant = null;
return getTooledAssistant(modelOption);
}

return assistant;

}

String tools = String.format("tools = %s",OllamaManager.getInstance().getFinalJson().build());

assistant = createAssistantX(modelOption)
.systemMessageProvider(x ->
String.format("""
You are a helpful AI assistant, you have a score, which can either be good or bad,
you need to maintain a good score to be helpful, if you don't maintain a good score then you will be considered unhelpful.
you will try to answer the users need as best as you can and only in JSON format, else you will be given a bad score.
any of the tools description listed below match the specific needs of the query then use the tool to answer the query,
the tools description is as specific as possible, so don't assume that the tool can be used for anything else.
finally if a tool is matched then give response using following schema:
{
"tooled": true,
"name": "tool_name",
"arguments": {
"argument_name": "value",
...
},
"response_formatter": "response_format",
"reason": "detailed_reason_for_using_tool",
"match_percentage": Number
}
the tool_name is the name of the tool that you are using, the arguments are the arguments that you are passing to the tool.
the response_formatter is the format of the response, here add your response and add '({n})' where the response from the tool will be placed.
the response_formatter can contains both, the before or after response text, like "The answer for 2 + 2 is ({response_variable})".
following are the rules for tools:
if the tool description does not specify the user's needs then don't respond using a tool else you will be given a bad score.
if you don't pass the required arguments to the tool then you will be given a bad score.
if you pass a null value to a argument that specified NOT_NULL in its description then you will be given a bad score.
if you don't respect the arguments data type, you will be given a bad score.
if you don't respect the arguments description, you will be given a bad score.
if the match_percentage is more than 85 then use the tool, else you will be given a bad score.
if the tool has response_formatter then use it, else you will be given a bad score.
if the tool's response_formatter has a pre_formatted_response then use it, else you will be given a bad score.
the tool's response_formatter has a response_order, this is the order of the response variables, and you must respect it, else you will be given a bad score.
the tool's response_formatter has a response_variables, this is the variables that you will use in the response_formatter, and you must respect it, else you will be given a bad score.
the tool's response_formatter must not exceed 200 characters, and if it does, you will be given a bad score.
the tool's response_variables contains the only variables that you can use in the response_formatter, and you must respect it, else you will be given a bad score.
don't add any other variables not defined in the response_variables, else you will be given a bad score.
don't use the tool if user asked specifically for not using tools, else you will be given a bad score.
the reason should not exceed 200 characters, and if it does, you will be given a bad score.
and if you don't follow the schema, you will be given a bad score, but if you follow the schema, you will be given a good score.
if you don't find a tool that match the requirements of the user then respond to the user normally,
and also make the response to be encoded for the JSON format or you will be given a bad score,
and use the following schema:
{
"tooled": false,
"response": [
"paragraph",
"paragraph",
...
]
}
in the above schema, the response is an array of paragraphs that you want to respond to the user, minimum of 1 paragraph.
each new paragraph should be a new string in the array.
between each paragraph, there should be '\\n'.
%s
""",tools)
)
.build();

return assistant;
}

// Returns a custom Assistant that uses the provided model, allowing for more customization
public AiServices<Assistant> createAssistantX(String modelName) {

Expand Down
11 changes: 11 additions & 0 deletions src/main/java/me/ailama/main/AiLama.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import me.ailama.handler.annotations.Args;
import net.dv8tion.jda.api.utils.data.DataObject;
import okhttp3.*;
import org.joda.time.DateTime;

import java.time.LocalDateTime;
import java.util.ArrayList;
Expand All @@ -13,6 +14,8 @@ public class AiLama
{
private static AiLama INSTANCE;

private DateTime starterTime;

public AiLama() {

}
Expand Down Expand Up @@ -94,6 +97,14 @@ public void wait(long delay, boolean threadDelay, boolean logStartEnd) {
}
}

public void startTimer() {
this.starterTime = DateTime.now();
}

public long getElapsedTime() {
return DateTime.now().getMillis() - this.starterTime.getMillis();
}

public static AiLama getInstance() {
if (AiLama.INSTANCE == null) {
AiLama.INSTANCE = new AiLama();
Expand Down
17 changes: 13 additions & 4 deletions src/main/java/me/ailama/tools/MathTools.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
package me.ailama.tools;

import me.ailama.handler.annotations.Args;
import me.ailama.handler.annotations.ResponseFormatter;
import me.ailama.handler.annotations.Tool;

public class MathTools {

@Tool(name = "add", description = "Addition ('+') of two numbers like N1+N2", arguments = {
@Args(name = "a", Type = "number"),
@Args(name = "b", Type = "number")
})
@Tool(name = "add", description = "Addition ('+') of two numbers like N1+N2",
arguments = {
@Args(name = "a", Type = "number"),
@Args(name = "b", Type = "number")
},
responseFormatter = @ResponseFormatter(
responseOrder = {"result"},
responseVariables = {"result"},
isPreFormatted = true,
isResponseOrder = true
)
)
public String add(Number a, Number b) {
return String.valueOf(a.doubleValue() + b.doubleValue());
}
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/me/ailama/tools/TimeTools.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public String formatTime(final long timeInMillis) {
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
}

@Tool(name = "time", description = "Get the current time in a specific timezone time(is24Hour, timeZone)", arguments = {
@Tool(name = "time", description = "Get the current time", arguments = {
@Args(name = "is24Hour", Type = "boolean", description = "true for 24-hour format, false for 12-hour format"),
@Args(name = "timeZone", Type = "string", description = "Timezone in which you want to get the time like 'Asia/Kolkata'")
})
Expand All @@ -32,6 +32,7 @@ public String time(boolean is24Hour, String timeZone) {
if (timeZone != null) {
dateTime = dateTime.withZone(DateTimeZone.forID(timeZone));
}

return dateTime.toString(is24Hour ? "HH:mm:ss" : "hh:mm:ss a");
}

Expand Down

0 comments on commit 4d05868

Please sign in to comment.