Moved system prompt to configuration file.
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Sun, 19 May 2024 20:52:38 +0000 (23:52 +0300)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Sun, 19 May 2024 20:52:38 +0000 (23:52 +0300)
alyverkko-cli.yaml
doc/index.org
src/main/java/eu/svjatoslav/alyverkko_cli/AiTask.java
src/main/java/eu/svjatoslav/alyverkko_cli/commands/MailCorrespondentCommand.java
src/main/java/eu/svjatoslav/alyverkko_cli/commands/MailQuery.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/alyverkko_cli/configuration/Configuration.java
src/main/java/eu/svjatoslav/alyverkko_cli/configuration/Prompt.java [new file with mode: 0644]

index 0e00b35..0fc9ede 100644 (file)
@@ -5,8 +5,31 @@ llama_cpp_executable_path: "/home/user/AI/llama.cpp/main"
 batch_thread_count: 10
 thread_count: 6
 models:
-  - alias: "wizard"
+  - alias: "default"
     filesystem_path: "WizardLM-2-8x22B.Q5_K_M-00001-of-00005.gguf"
     context_size_tokens: 64000
     end_of_text_marker: null
+  - alias: "maid"
+    filesystem_path: "daringmaid-20b.Q4_K_M.gguf"
+    context_size_tokens: 4096
+    end_of_text_marker: null
+prompts:
+  - alias: "default"
+    prompt: |
+      This conversation involves a user and AI assistant where the AI
+      is expected to provide not only immediate responses but also detailed and
+      well-reasoned analysis. The AI should consider all aspects of the query
+      and deliver insights based on logical deductions and comprehensive understanding.
+      AI assistant should reply using emacs org-mode syntax.
+      Quick recap: *this is bold* [[http://domain.org][This is link]]
+      * Heading level 1
+      ** Heading level 2
+      | Col 1 Row 1 | Col 2 Row 1 |
+      | Col 1 Row 2 | Col 2 Row 2 |
+      #+BEGIN_SRC python
+        print ('Hello, world!')
+      #+END_SRC
 
+  - alias: "writer"
+    prompt: |
+      You are best-selling book writer.
index 3670b99..16bba29 100644 (file)
@@ -165,7 +165,7 @@ file. Below is an example of how the configuration file might look:
   batch_thread_count: 10
   thread_count: 6
   models:
-    - alias: "wizard"
+    - alias: "default"
       filesystem_path: "WizardLM-2-8x22B.Q5_K_M-00001-of-00005.gguf"
       context_size_tokens: 64000
       end_of_text_marker: null
@@ -173,7 +173,26 @@ file. Below is an example of how the configuration file might look:
       filesystem_path: "daringmaid-20b.Q4_K_M.gguf"
       context_size_tokens: 4096
       end_of_text_marker: null
-
+  prompts:
+    - alias: "default"
+      prompt: |
+        This conversation involves a user and AI assistant where the AI
+        is expected to provide not only immediate responses but also detailed and
+        well-reasoned analysis. The AI should consider all aspects of the query
+        and deliver insights based on logical deductions and comprehensive understanding.
+        AI assistant should reply using emacs org-mode syntax.
+        Quick recap: *this is bold* [[http://domain.org][This is link]]
+        ,* Heading level 1
+        ,** Heading level 2
+        | Col 1 Row 1 | Col 2 Row 1 |
+        | Col 1 Row 2 | Col 2 Row 2 |
+        ,#+BEGIN_SRC python
+          print ('Hello, world!')
+        ,#+END_SRC
+
+    - alias: "writer"
+      prompt: |
+        You are best-selling book writer.
 #+end_src
 
 *** Configuration file syntax
@@ -209,9 +228,7 @@ Here are available parameters:
   keep CPU cores unnecessarily busy.
   - Default value: 6
 
-- models :: List of available large language models. First model in
-  the list would be used by default.
-
+- models :: List of available large language models.
   - alias :: Short model alias.
   - filesystem_path :: File name of the model as located within
     *models_directory*
@@ -222,6 +239,19 @@ Here are available parameters:
     identify and remove them so that they don't leak into
     conversation. Default value is: *null*.
 
+- prompts :: List of predefined system prompts for AI.
+  - alias :: Short prompt alias.
+  - prompt :: Actual prompt that will be sent to AI alongside actual
+    user question.
+
+
+*WARNING: MODEL SELECTION AND PROMPT SELECTION IS CURRENTLY NOT IMPLEMENTED*
+
+While it is possible to configure many prompts and models, at the
+moment Ã„lyverkko CLI will always choose model and prompt with
+"default" alias. This is going to be fixed soon.
+
+
 *** Enlisting available models
 Once Ã„lyverkko CLI is installed and properly configured, you can run
 following command at commandline to see what models are available to
index 1c5a608..2a7ca34 100644 (file)
@@ -1,5 +1,6 @@
 package eu.svjatoslav.alyverkko_cli;
 
+import eu.svjatoslav.alyverkko_cli.commands.MailQuery;
 import eu.svjatoslav.alyverkko_cli.model.Model;
 
 import java.io.*;
@@ -12,50 +13,31 @@ public class AiTask {
     public static final String AI_RESPONSE_MARKER = "ASSISTANT:";
     private static final String LLAMA_CPP_META_INFO_MARKER = "llm_load_print_meta: ";
 
-    private final String aiQuery;
     private final Model model;
     private final Float temperature;
+    private final String systemPrompt;
+    private final String userPrompt;
     File inputFile;
 
     /**
      * Creates a new AI task.
-     *
-     * @param input       Problem statement to be used for the AI task.
-     * @param model       The model to be used for the AI task.
-     * @param temperature The temperature to be used for the AI inference process.
      */
-    public AiTask(String input, Model model, Float temperature) {
-        this.aiQuery = buildAiQuery(input);
-        this.model = model;
-        this.temperature = temperature == null ? configuration.getDefaultTemperature() : temperature;
+    public AiTask(MailQuery mailQuery) {
+        this.model = mailQuery.model;
+        this.temperature = configuration.getDefaultTemperature();
+        this.systemPrompt = mailQuery.systemPrompt;
+        this.userPrompt  = mailQuery.userPrompt;
     }
 
-    private String buildAiQuery(String input) {
+    private String buildAiQuery() {
         StringBuilder sb = new StringBuilder();
+        sb.append("SYSTEM:\n").append(systemPrompt).append("\n");
 
-        sb.append("SYSTEM:\nThis conversation involves a user and AI assistant where the AI " +
-                "is expected to provide not only immediate responses but also detailed and " +
-                "well-reasoned analysis. The AI should consider all aspects of the query " +
-                "and deliver insights based on logical deductions and comprehensive understanding." +
-                "AI assistant should reply using emacs org-mode syntax.\n" +
-                "Quick recap: *this is bold* [[http://domain.org][This is link]]\n" +
-                "* Heading level 1\n" +
-                "** Heading level 2\n" +
-                "| Col 1 Row 1 | Col 2 Row 1 |\n" +
-                "| Col 1 Row 2 | Col 2 Row 2 |\n" +
-                "#+BEGIN_SRC python\n" +
-                "  print ('Hello, world!')\n" +
-                "#+END_SRC\n\n");
-
-
-        String filteredInput = filterParticipantsInUserInput(input);
-
-        // if filtered input does not start with "USER:", add it
-        if (!filteredInput.startsWith("USER:")) {
-            filteredInput = "USER:\n" + filteredInput;
-        }
+        String filteredUserPrompt = filterParticipantsInUserInput(userPrompt);
+        if (!filteredUserPrompt.startsWith("USER:")) sb.append("USER:\n");
+        sb.append(filteredUserPrompt).append("\n");
 
-        sb.append(filteredInput).append("\n").append(AI_RESPONSE_MARKER);
+        sb.append(AI_RESPONSE_MARKER);
         return sb.toString();
     }
 
@@ -92,7 +74,7 @@ public class AiTask {
      */
     public String runAiQuery() throws InterruptedException, IOException {
         try {
-            initializeInputFile();
+            initializeInputFile(buildAiQuery());
 
             ProcessBuilder processBuilder = new ProcessBuilder();
             processBuilder.command(getCliCommand().split("\\s+")); // Splitting the command string into parts
@@ -112,7 +94,7 @@ public class AiTask {
     /**
      * Initializes the input file for the AI task.
      */
-    private void initializeInputFile() throws IOException {
+    private void initializeInputFile(String aiQuery ) throws IOException {
         // write AI input to file
         inputFile = createTemporaryFile();
         Files.write(inputFile.toPath(), aiQuery.getBytes());
@@ -243,9 +225,5 @@ public class AiTask {
         }
     }
 
-    public static String runAiQuery(String problemStatement, Model model, Float temperature) throws IOException, InterruptedException {
-        AiTask ai = new AiTask(problemStatement, model, temperature);
-        return ai.runAiQuery();
-    }
 
 }
index 49e44b0..d694025 100644 (file)
@@ -1,7 +1,6 @@
 package eu.svjatoslav.alyverkko_cli.commands;
 
 import eu.svjatoslav.alyverkko_cli.*;
-import eu.svjatoslav.alyverkko_cli.model.Model;
 import eu.svjatoslav.alyverkko_cli.model.ModelLibrary;
 import eu.svjatoslav.commons.cli_helper.parameter_parser.Parser;
 import eu.svjatoslav.commons.cli_helper.parameter_parser.parameter.DirectoryOption;
@@ -77,35 +76,39 @@ public class MailCorrespondentCommand implements Command {
         System.out.println("\nReplying to mail: " + file.getName());
 
         // Read the mail contents, and remove the TOCOMPUTE: prefix from the first line
-        String mailContents = getFileContentsAsString(file);
-        mailContents = removeToComputePrefixFile(mailContents);
+        String inputFileContent = getFileContentsAsString(file);
+        MailQuery mailQuery = parseInputFileContent(inputFileContent);
 
-        // faster model for testing for development time testing
-        // String modelAlias = "maid";
-        // TODO: make model CLI argument
-        String modelAlias = "wizard";
+        AiTask aiTask = new AiTask(mailQuery);
+        String aiGeneratedResponse = aiTask.runAiQuery();
 
-        Model model = modelLibrary.findModelByAlias(modelAlias).get();
-        String aiGeneratedResponse = AiTask.runAiQuery(mailContents, model, null);
+        // Prepare result file content
+        StringBuilder resultFileContent = new StringBuilder();
+        if (!mailQuery.userPrompt.startsWith("* USER:\n")) resultFileContent.append("* USER:\n");
+        resultFileContent.append(mailQuery.userPrompt).append("\n");
+        resultFileContent.append("* ASSISTANT:\n").append(aiGeneratedResponse).append("\n");
 
-        // Append the AI response to the mail contents
-        if (!mailContents.startsWith("* USER:\n")) {
-            mailContents = "* USER:\n" + mailContents;
-        }
-
-        String newMailContents = mailContents + "\n* ASSISTANT:\n" + aiGeneratedResponse;
-
-        // Write the result to the file
-        saveToFile(file, newMailContents);
+        // Write result content to the file
+        saveToFile(file, resultFileContent.toString());
     }
 
-    private String removeToComputePrefixFile(String mailContents) {
-        // Remove the first line from the mail contents
-        int firstNewLineIndex = mailContents.indexOf('\n');
-        if (firstNewLineIndex != -1) {
-            mailContents = mailContents.substring(firstNewLineIndex + 1);
+    private MailQuery parseInputFileContent(String inputFileContent) {
+        MailQuery mailQuery = new MailQuery();
+
+        // deduct user prompt
+        int firstNewLineIndex = inputFileContent.indexOf('\n');
+        if (firstNewLineIndex == -1) {
+            throw new IllegalArgumentException("Input file is only one line long. This is the content: " + inputFileContent);
+        } else {
+            mailQuery.userPrompt = inputFileContent.substring(firstNewLineIndex + 1);
         }
-        return mailContents;
+
+        String firstLine = inputFileContent.substring(0, firstNewLineIndex);
+        //System.out.println("First line is: \"" + firstLine + "\"");
+
+        mailQuery.systemPrompt = configuration.getPromptByAlias("default");
+        mailQuery.model = modelLibrary.findModelByAlias("default").get();
+        return mailQuery;
     }
 
 
diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/commands/MailQuery.java b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/MailQuery.java
new file mode 100644 (file)
index 0000000..0704ab2
--- /dev/null
@@ -0,0 +1,9 @@
+package eu.svjatoslav.alyverkko_cli.commands;
+
+import eu.svjatoslav.alyverkko_cli.model.Model;
+
+public class MailQuery {
+    public String systemPrompt;
+    public String userPrompt;
+    public Model model;
+}
index 91ba191..550d03c 100644 (file)
@@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 
 import java.io.*;
 import java.util.List;
+import java.util.Map;
 
 public class Configuration {
 
@@ -30,6 +31,11 @@ public class Configuration {
     @JsonProperty("thread_count")
     private int threadCount;
 
+    @JsonProperty("prompts")
+    private List<Prompt> prompts;
+
+    private List<ConfigurationModel> models;
+
     public List<ConfigurationModel> getModels() {
         return models;
     }
@@ -38,8 +44,6 @@ public class Configuration {
         this.models = models;
     }
 
-    private List<ConfigurationModel> models;
-
     public Configuration() {
     }
 
@@ -63,6 +67,13 @@ public class Configuration {
         return mapper.readValue(configFile, Configuration.class);
     }
 
+    public List<Prompt> getPrompts() {
+        return prompts;
+    }
+
+    public void setPrompts(List<Prompt> prompts) {
+        this.prompts = prompts;
+    }
 
     public File getMailDirectory() {
         return mailDirectory;
@@ -111,4 +122,16 @@ public class Configuration {
     public void setThreadCount(int threadCount) {
         this.threadCount = threadCount;
     }
+
+    public String getPromptByAlias(String alias) {
+        //System.out.println("Prompts: " + prompts);
+
+        for (Prompt prompt : prompts) {
+            if (prompt.getAlias().equals(alias)) {
+                return prompt.getPrompt();
+            }
+        }
+
+        return null;
+    }
 }
diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/Prompt.java b/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/Prompt.java
new file mode 100644 (file)
index 0000000..5c123ef
--- /dev/null
@@ -0,0 +1,31 @@
+package eu.svjatoslav.alyverkko_cli.configuration;
+
+public class Prompt {
+
+    private String alias;
+    private String prompt;
+
+    public String getAlias() {
+        return alias;
+    }
+
+    public void setAlias(String alias) {
+        this.alias = alias;
+    }
+
+    public String getPrompt() {
+        return prompt;
+    }
+
+    public void setPrompt(String prompt) {
+        this.prompt = prompt;
+    }
+
+    @Override
+    public String toString() {
+        return "Prompt{" +
+                "alias='" + alias + '\'' +
+                ", prompt='" + prompt + '\'' +
+                '}';
+    }
+}