Advanced Prompt Processing in Java : Parallel Processing, Data Conversion and more..
Tags: webdev, java, ai
Published: 2024-03-26 22:07:19

Tools4AI is an open-source project that connects Large Language Models (LLMs) like Gemini/OpenAI/Mistral with Java. It's designed to perform actions based on what people ask in their prompts. In this article, we're focusing on two main parts of the Tools4AI project: the `ActionProcessor`and the `PromptTransformer`.

> Code for the project is [here](https://github.com/vishalmysore/sam)

**ActionProcessor**
The ActionProcessor is key to making things happen in Tools4AI. It looks at what someone asks for in plain language, figures out what action is needed, and then does it. This could mean running a piece of Java code, making a web request, executing a shell script, making HTTP rest call, or getting something from a configuration. It's all about taking a prompt and turning it into an action smoothly and accurately. It can process multiple prompts simultaneously or in dependency order.

```java
String cookPromptSingleText = "My friends name is Vishal ," +
                "I dont know what to cook for him today.";
ActionProcessor processor = new ActionProcessor();
String result = (String)processor.processSingleAction(cookPromptSingleText);
log.info(result);
```

The above code uses Gemini to process the action. Action is called dynamically in real time based on prediction. Below is the example of implementing action in Java

```java

@Predict(actionName = "whatFoodDoesThisPersonLike", description = "what is the food preference of this person ")
public class SimpleAction implements JavaMethodAction {

    public String whatFoodDoesThisPersonLike(String name) {
        if("vishal".equalsIgnoreCase(name))
            return "Paneer Butter Masala";
        else if ("vinod".equalsIgnoreCase(name)) {
            return "aloo kofta";
        }else
            return "something yummy";
    }

}

```

Instead of Gemini you can also use Open AI to call the same action based on prompts.

```java
OpenAiActionProcessor opeAIprocessor = new OpenAiActionProcessor();
Sring result = (String)opeAIprocessor.processSingleAction('My friends name is Vishal ,he lives in tornto.I want save this info locally');
System.out.println(result);

```

**Prompts with Multiple Actions**
Action Processors are designed with the capability to handle complex prompts that encompass multiple actions. When faced with a lengthy prompt containing various instructions, the system cleverly breaks it down into manageable subprompts. This subdivision allows each action within the larger prompt to be addressed individually, ensuring a thorough and accurate execution of tasks. Through this approach, the processors maintain efficiency and effectiveness, regardless of the prompt's complexity, by systematically tackling each component of the instruction set. This feature highlights the processors' advanced understanding and flexibility, enabling them to adapt to a wide range of user requests with precision.

```java

public class ActionExample {
    public static void main(String[] args) throws AIProcessingException {
        ActionProcessor processor = new ActionProcessor();
        String multiPrmpt = "hey I am in Toronto do you think i can go out without jacket," +
                " also save the weather information , City location and your suggestion in file, " +
                "also include places to see";
        String processed = processor.processMultipleActionDynamically
                (multiPrmpt, 
                        new LoggingHumanDecision(),
                        new LogginggExplainDecision());
        log.info(processed);
    }
}
```

Once a lengthy prompt is divided into smaller subprompts by the Action Processors, each subprompt is processed in a way that respects the logical sequence of actions. This means that if the subprompts are independent of one another, they can be handled separately without any particular order. However, when the subprompts are dependent, indicating that the outcome of one action influences another, the processors smartly arrange to execute these in a dependency order.

```json
{
  "prmpt": [
    {
      "id": "1",
      "subprompt": "What is the weather in Toronto?",
      "depend_on": null
    },
    {
      "id": "2",
      "subprompt": "Do I need a jacket in this weather?",
      "depend_on": "1"
    },
    {
      "id": "3",
      "subprompt": "Save the weather information, city location, and suggestion in a file.",
      "depend_on": "2"
    },
    {
      "id": "4",
      "subprompt": "Suggest some places to see in Toronto.",
      "depend_on": "3"
    }
  ]
} 

```

In cases where the actions are interlinked, the result from a previously executed subprompt serves as input for the subsequent one. This sequential processing ensures that each step is informed by the outcome of the prior action, maintaining the integrity and coherence of the overall task being accomplished. This methodical approach underlines the processors' capability to adapt to both simple and complex sets of instructions, effectively handling tasks that require a nuanced understanding of context and sequence.

**PromptTransformer**

The Prompt Transformer is another key component of the Tools4AI project, designed to make data transformation tasks straightforward and efficient. This feature allows for the easy conversion of prompts into different formats, including Java Plain Old Java Objects (POJOs), JSON strings, CSV files, and XML. By facilitating the direct transformation of prompts into domain-specific objects, the Prompt Transformer simplifies and accelerates the process of handling data. Its ability to adapt data structures to a variety of requirements makes it an invaluable tool for modern applications, providing both flexibility and convenience in managing and manipulating data.

**Convert Prompt to Simple Pojo**
Lets take the first scenario where you want to convert the prompt directly into Java Bean or Pojo

```java

PromptTransformer builder = new PromptTransformer();
String promptTxt ="Sachin Tendulkar is very good cricket player, " +
                           "he joined the sports on 24032022, he has played 300 matches " +
                           "and his max score is 400";
//Convert the prompt to Pojo
Player player = (Player)builder.transformIntoPojo(promptTxt, Player.class.getName(),"Player","create player pojo");
log.info(player.toString());

```


The above will convert the prompt into this simple Pojo

```java

public class Player {
     int matches;
     int maxScore;
     String firstName;
     String lastName;
     Date dateJoined;


}
```


**Convert Prompt to Complex Pojo**
The transformer can also convert into complex Pojo ( where there are multiple objects inside the Pojo)

```java
PromptTransformer builder = new PromptTransformer();
promptTxt = "can you book Maharaja restaurant in " +
            "Toronto for 4 people on 12th may , I am Vishal ";
//Convert the prompt to Complex Pojo
RestaurantPojo pojo = (RestaurantPojo)builder.transformIntoPojo(promptTxt, RestaurantPojo.class.getName(),"RestaurantPojo","Build the pojo for restaurant");
log.info(pojo.toString()); 
This will create the Pojo Object of RestaurantPojo and also populate the internal objects ( not just primitive)
public class RestaurantPojo {
    String name;
    int numberOfPeople;
    //Pojo inside Pojo    
    RestaurantDetails restaurantDetails;
    boolean cancel;
    String reserveDate; 
```

**Convert Prompt with Custom GSON**

If you expect some custom objects like Date etc in the prompt you can have custom Gson Builder

```java
//Using Custom GSON to convert special values
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Date.class, new DateDeserializer("dd MMMM yyyy"));
Gson gson = gsonBuilder.create();
PromptTransformer customBuilder = new PromptTransformer(gson);
String prompt = "Sachin Tendulkar is very good cricket player, he joined the sports on 12 May 2008," +
                "he has played 300 matches and his max score is 400";
player = (Player)customBuilder.transformIntoPojo(prompt, Player.class.getName(),"Player","create player pojo");
log.info(player.toString()); 
This will use Custom Date Serializer
public class DateDeserializer implements JsonDeserializer<Date> {
    private final DateFormat dateFormat;

    public DateDeserializer(String format) {
        this.dateFormat = new SimpleDateFormat(format, Locale.ENGLISH);
    }

    @Override
    public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        try {
            return dateFormat.parse(json.getAsString().replaceAll("(st|nd|rd|th),", ","));
        } catch (ParseException e) {
            throw new JsonParseException(e);
        }
    }
} 

```

**Convert Prompt to Json String**

If you want to convert the Prompt into the Json String

```java

prompt = "Sachin Tendulkar is very good cricket player, he joined the sports on 12 May 2008," +
                "he has played 300 matches and his max score is 400";
 //Extract Json from the prompt
 String jsonString = "{\"lastName\":\"String\",\"firstName\":\"String\"}";
 jsonString = builder.transformIntoJson(jsonString,prompt,"player","get player details");
 log.info(jsonString); 
The result will be
"{\"lastName\":\"Tendulkar\",\"firstName\":\"Sachin\"}";
``` 

You can extract custom parameters from the prompt or can convert the entire prompt into JSON

**Convert Prompt to XML**

Once you have the JSON you can convert the JSON into XML

```java
JSONObject jsonObject = new JSONObject(jsonNode.toString());
String xmlString = XML.toString(jsonObject); 
Usage
```

Both the PromptTransformer and ActionProcessor components are versatile tools that can be integrated into any Java project, whether it's a standalone application or one based on the Spring Boot framework. These components are designed to enhance the functionality of Java applications by enabling them to process natural language prompts and execute corresponding actions in a structured and efficient manner.
Integration in Standalone Java Projects
In standalone Java projects, integrating these components involves adding the necessary libraries or source code to your project's build path. Once integrated, you can instantiate and use these components directly within your application code to leverage their capabilities for processing prompts and executing actions based on natural language understanding.

**Integration in Spring Boot Projects**

For projects built with Spring Boot, integration can be streamlined through Spring's dependency injection and auto-configuration capabilities. You might need to create configuration beans for `PromptTransformer `and `ActionProcessor` to ensure they are properly initialized and available for use across your Spring Boot application. Additionally, leveraging Spring Boot's properties files can provide a flexible way to configure the behavior of these components dynamically.
For more detailed guidance on integrating these components into Spring Boot applications, referring to previous articles or documentation that specifically addresses Spring Boot use cases would be beneficial. These resources can provide step-by-step instructions and best practices for harnessing the full potential of PromptTransformer and ActionProcessor within the Spring Boot ecosystem, ensuring seamless integration and optimal performance.
Whether your project is a simple Java application or a comprehensive Spring Boot application, these components offer valuable functionalities for enhancing your application's interaction with natural language data, streamlining data transformation tasks, and executing complex actions based on user prompts.