From 0f54672862d9c8c6ea66f1aef124edf56672ffd7 Mon Sep 17 00:00:00 2001 From: Svjatoslav Agejenko Date: Mon, 1 Dec 2025 00:45:53 +0200 Subject: [PATCH] Initial commit --- .gitignore | 13 + COPYING | 121 +++ alyverkko-cli | 6 + alyverkko-cli.yaml | 16 + doc/QwQ-32B Termination issue.pdf | Bin 0 -> 176351 bytes doc/index.org | 855 ++++++++++++++++++ doc/pausing and resuming.pdf | Bin 0 -> 196957 bytes install | 96 ++ launchers/alyverkko-cli-pause.desktop | 8 + launchers/alyverkko-cli-resume.desktop | 8 + launchers/alyverkko-cli.desktop | 8 + logo.png | Bin 0 -> 4012 bytes maven.xml | 15 + pom.xml | 209 +++++ .../eu/svjatoslav/alyverkko_cli/Command.java | 32 + .../eu/svjatoslav/alyverkko_cli/Main.java | 83 ++ .../eu/svjatoslav/alyverkko_cli/Utils.java | 23 + .../commands/JoinFilesCommand.java | 215 +++++ .../commands/ListModelsCommand.java | 53 ++ .../alyverkko_cli/commands/WizardCommand.java | 482 ++++++++++ .../alyverkko_cli/commands/package-info.java | 13 + .../commands/task_processor/Task.java | 144 +++ .../commands/task_processor/TaskProcess.java | 298 ++++++ .../task_processor/TaskProcessorCommand.java | 545 +++++++++++ .../commands/task_processor/package-info.java | 12 + .../configuration/Configuration.java | 118 +++ .../configuration/ConfigurationHelper.java | 58 ++ .../configuration/ConfigurationModel.java | 62 ++ .../configuration/SkillConfig.java | 29 + .../configuration/package-info.java | 13 + .../svjatoslav/alyverkko_cli/model/Model.java | 109 +++ .../alyverkko_cli/model/ModelLibrary.java | 131 +++ .../alyverkko_cli/model/package-info.java | 12 + .../alyverkko_cli/package-info.java | 14 + tools/implement idea | 12 + tools/open with IntelliJ IDEA | 54 ++ tools/update web site | 35 + uninstall | 13 + 38 files changed, 3915 insertions(+) create mode 100644 .gitignore create mode 100644 COPYING create mode 100755 alyverkko-cli create mode 100644 alyverkko-cli.yaml create mode 100644 doc/QwQ-32B Termination issue.pdf create mode 100644 doc/index.org create mode 100644 doc/pausing and resuming.pdf create mode 100755 install create mode 100644 launchers/alyverkko-cli-pause.desktop create mode 100644 launchers/alyverkko-cli-resume.desktop create mode 100644 launchers/alyverkko-cli.desktop create mode 100644 logo.png create mode 100644 maven.xml create mode 100644 pom.xml create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/Command.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/Main.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/Utils.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/commands/JoinFilesCommand.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/commands/ListModelsCommand.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/commands/WizardCommand.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/commands/package-info.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/commands/task_processor/Task.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/commands/task_processor/TaskProcess.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/commands/task_processor/TaskProcessorCommand.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/commands/task_processor/package-info.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/configuration/Configuration.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/configuration/ConfigurationHelper.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/configuration/ConfigurationModel.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/configuration/SkillConfig.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/configuration/package-info.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/model/Model.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/model/ModelLibrary.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/model/package-info.java create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/package-info.java create mode 100755 tools/implement idea create mode 100755 tools/open with IntelliJ IDEA create mode 100755 tools/update web site create mode 100755 uninstall diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..52e4c3b --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +/.idea/ +/.settings/ +/target/ +/*.iml +/*.log +/test/ + +/doc/apidocs/ +/doc/graphs/ +/doc/index.html +/doc/setup.html + + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/COPYING @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/alyverkko-cli b/alyverkko-cli new file mode 100755 index 0000000..aea6d86 --- /dev/null +++ b/alyverkko-cli @@ -0,0 +1,6 @@ +#!/bin/bash + +set -f + +java -Xmx4500m -classpath /opt/alyverkko-cli/* eu.svjatoslav.alyverkko_cli.Main "$@" + diff --git a/alyverkko-cli.yaml b/alyverkko-cli.yaml new file mode 100644 index 0000000..f931d6e --- /dev/null +++ b/alyverkko-cli.yaml @@ -0,0 +1,16 @@ +task_directory: "/home/user/AI/tasks" +models_directory: "/home/user/AI/models" +default_temperature: 0.7 +llama_cpp_dir_path: "/home/user/AI/llama.cpp/" +batch_thread_count: 10 +thread_count: 6 +skills_directory: "/home/user/.config/alyverkko-cli/skills" +models: + - alias: "default" + filesystem_path: "WizardLM-2-8x22B.Q5_K_M-00001-of-00005.gguf" + context_size_tokens: 64000 + end_of_text_marker: null + - alias: "mistral" + filesystem_path: "Mistral-Large-Instruct-2407.Q8_0.gguf" + context_size_tokens: 32768 + end_of_text_marker: null diff --git a/doc/QwQ-32B Termination issue.pdf b/doc/QwQ-32B Termination issue.pdf new file mode 100644 index 0000000000000000000000000000000000000000..827eb5d106322f6f0116ebd208e226d94e63b850 GIT binary patch literal 176351 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.0, 1.0 = no + penalty) + +**** Performance Tuning + +- =thread_count=: Sets the number of threads to be used by the AI + during response generation. RAM data transfer speed is usually + bottleneck here. When RAM bandwidth is saturated, increasing thread + count will no longer increase processing speed, but it will still + keep CPU cores unnecessarily busy. + +- =batch_thread_count=: Specifies the number of threads to use for + input prompt processing. CPU computing power is usually the + bottleneck here. + +**** Model-Specific Settings + +Each model in the =models= list can have: + +- =alias=: Short model alias. Model with alias "default" would be used + by default. + +- =filesystem_path=: File name of the model as located within + *models_directory* + +- =context_size_tokens=: Context size in tokens that model was trained + on. + +- =end_of_text_marker=: Some models produce certain markers to + indicate end of their output. If specified here, Älyverkko CLI can + identify and remove them so that they don't leak into + conversation. Default value is: *null*. + +- =temperature=, =top_p=, =repeat_penalty=: Model-specific overrides + +*** Configuration file example + +The application is configured using a YAML-formatted configuration +file. Below is an example of how the configuration file might look: + +#+begin_src yaml + task_directory: "/home/user/AI/tasks" + models_directory: "/home/user/AI/models" + skills_directory: "/home/user/AI/skills" + llama_cli_path: "/home/user/AI/llama.cpp/build/bin/llama-cli" + + # Processing parameters + default_temperature: 0.7 + default_top_p: 0.9 + default_repeat_penalty: 1.0 + thread_count: 6 + batch_thread_count: 10 + + # Model definitions + models: + - alias: "default" + filesystem_path: "model.gguf" + context_size_tokens: 64000 + end_of_text_marker: null + temperature: 0.8 # Optional model-specific parameter + top_p: 0.9 # Optional + repeat_penalty: 1.1 # Optional + - alias: "mistral" + filesystem_path: "Mistral-Large-Instruct-2407.Q8_0.gguf" + context_size_tokens: 32768 + end_of_text_marker: null +#+end_src + +*** Parameter Precedence Hierarchy + +For *temperature*, *top_p*, and *repeat_penalty* parameters, values +are determined using this priority order (highest to lowest): + +1. *Skill-specific value* (from skill's YAML file) +2. *Model-specific value* (from model configuration) +3. *Global default value* (from main configuration) + +This allows fine-grained control where more specific configurations +override broader ones. + +*** 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 +it: + +: alyverkko-cli listmodels + +Note: Models that reference missing files will be automatically marked +with "-missing" suffix in their alias by configuration wizard. You can +manually remove this suffix after fixing the model file path. + +*** Self test +The *selftest* command performs a series of checks to ensure the +system is configured correctly: + +: alyverkko-cli selftest + +It verifies: +- Configuration file integrity. +- Model directory existence. +- The presence of the *llama.cpp* executable. + +** Skill concept and configuration +*** Skill File Format + +Skills are defined in YAML files stored in the *skills_directory*. + +Each skill file contains: + +#+begin_src yaml +prompt: "Full system prompt text here" +temperature: 0.8 # Optional +top_p: 0.9 # Optional +repeat_penalty: 1.1 # Optional +#+end_src + +The system prompt must contain ** which gets replaced with +the actual user prompt during execution. + +*** Example Skill File +: writer.yaml + +#+begin_src yaml + temperature: 0.9 + top_p: 0.95 + prompt: | + <|im_start|>system + User will provide you with task that needs to be solved along with + existing relevant information. + + You are artificial general intelligence system that always provides well reasoned responses. + <|im_end|> + <|im_start|>user + /think Solve following problem: + + + + <|im_end|> + <|im_start|>assistant +#+end_src + +** Starting process daemon + +Älyverkko CLI keeps continuously listening for and processing tasks +from a specified mail directory. + +There are multiple alternative ways to start Älyverkko CLI in mail +processing mode: + +**** Start via command line interface + +1. Open your terminal. + +2. Run the command: + : alyverkko-cli process + +3. The application will start monitoring the configured mail directory + for incoming messages and process them accordingly in endless loop. + +4. To terminate Älyverkko CLI, just hit *CTRL+c* on the keyboard, or + close terminal window. + +**** Start using your desktop environment application launcher + +1. Access the application launcher or application menu on your desktop + environment. + +2. Search for "Älyverkko CLI". + +3. Click on the icon to start the application. It will open its own + terminal. + +4. If you want to stop Älyverkko CLI, just close terminal window. + +**** Start in the background as systemd system service + +During Älyverkko CLI [[id:0b705a37-9b84-4cd5-878a-fedc9ab09b12][installation]], installation script will prompt you +if you want to install *systemd* service. If you chose *Y*, Alyverkko +CLI would be immediately started in the background as a system +service. Also it will be automatically started on every system reboot. + +To view service status, use: +: systemctl -l status alyverkko-cli + +If you want to stop or disable service, you can do so using systemd +facilities: + +: sudo systemctl stop alyverkko-cli +: sudo systemctl disable alyverkko-cli + +* Usage +** Task file format + +Task files follow a specific structure that begins with a header line: + +#+begin_example +TOCOMPUTE: [parameters] +[User prompt content] +#+end_example + +*** Task File Header Format + +The first line *must* begin with exactly =TOCOMPUTE:= followed by +space-separated key-value pairs: + +#+begin_example +TOCOMPUTE: skill=default model=mistral priority=10 +#+end_example + +Valid parameters in the header: +- =skill=[name]=: Specifies which skill to use (defaults to "default") +- =model=[alias]=: Specifies which AI model to use (defaults to "default") +- =priority=[integer]=: Higher integers mean higher priority (default: 0) + +*** Processed File Format + +After AI processing completes, a new file is created with: +1. First line: =DONE: skill=[name] model=[alias] duration=[time]= +2. =* USER:= section containing original user prompt +3. =* ASSISTANT:= section containing AI response + +: DONE: skill=writer model=default duration=5m +: ... + +** Task preparation +:PROPERTIES: +:ID: 4b7900e4-77c1-45e7-9c54-772d0d3892ea +:END: + +The Älyverkko CLI application expects input files for processing in +the form of plain text files within the specified tasks directory +(configured in the [[id:0fcdae48-81c5-4ae1-bdb9-64ae74e87c45][YAML configuration file]]). + +Suggested usage flow is to prepare AI assignments within the Älyverkko +CLI mail directory using normal text editor. Once AI assignment is +ready for processing, you should [[id:883d6e7c-60e0-422b-8c00-5cdc9dfec20d][initiate AI processing]] on that file. + +*** "joinfiles" command +*Note:* See also alternative solution with similar goal: [[https://github.com/aerugo/prelude][prelude]]. + +The *joinfiles* command is a utility for aggregating the contents of +multiple files into a single document, which can then be processed by +AI. This is particularly useful for preparing comprehensive problem +statements from various source files, such as software project +directories or collections of text documents. + +**** Usage + +To use the *joinfiles* command, specify the source directory +containing the files you wish to join and a topic name that will be +used to generate the output file name: + +#+begin_example +alyverkko-cli joinfiles -s /path/to/source/directory -t "my_topic" +#+end_example + +If desired, you can also specify a glob pattern to match only certain files within the directory: + +#+begin_example +alyverkko-cli joinfiles -s /path/to/source/directory -p "*.java" -t "my_topic" +#+end_example + +After joining the files, you can choose to open the resulting document +in text editor for further editing or review: + +#+begin_example +alyverkko-cli joinfiles -t "my_topic" --edit +#+end_example + +**** Options + +- **-s, --src-dir**: Specifies the source directory from which to join + files. + +- **-p, --pattern**: An optional glob pattern to match specific files + within the source directory. + +- **-t, --topic**: The topic name that will be used as a basis for the + output file name and should reflect the subject matter of the joined + content. + +- **-e, --edit**: Opens the joined file in text editor after the join + operation is complete. + +**** Example Use Case + +Imagine you have a software project with various source files that you +want to analyze using AI. You can use the *joinfiles* command to +create a single document for processing: + +#+begin_example +alyverkko-cli joinfiles -s /path/to/project -p "*.java" -t "software_analysis" --edit +#+end_example + +This will recursively search the project directory for Java source +files, aggregate their contents into a file named +*software_analysis.org* (within AI processor input files directory), +and open text editor on the file, so that you can add your analysis +instructions or problem statement. Finally you [[id:883d6e7c-60e0-422b-8c00-5cdc9dfec20d][Initiate AI processing]] +and after some time, you will get results and the end of the file. + +** Initiate AI processing +:PROPERTIES: +:ID: 883d6e7c-60e0-422b-8c00-5cdc9dfec20d +:END: + +Once your task file is prepared, you should place *TOCOMPUTE:* marker +on the first line of that file, so that it will be considered for +processing. + +When the Älyverkko CLI detects a new or modified file in the mail +directory: + +1. It checks if file has "TOCOMPUTE:" on the first line. If no, file + is ignored. Otherwise Älyverkko CLI continues processing the file. + +2. It reads the content of the file and feeds it as an input for an AI + model to generate a response. + +4. Once the AI has generated a response, the application appends this + response to the original mail contents within the same file, using + org-mode syntax to distinguish between the user's query and the + assistant's reply. The updated file will contain both the original + query (prefixed with: "* USER:*") and the AI's response (prefixed + with "* ASSISTANT:"), ensuring a clear and organized conversation + thread. "TOCOMPUTE:" is removed from the beginning of the file to + avoid processing same file again. + +Note: During AI task file preparation, feel free to save intermediary +states as often as needed because AI engine will keep ignoring the +file until *TOCOMPUTE:* line appears. Once AI assignment is ready, add +: TOCOMPUTE: +to the beginning of the file and save one last time. Älyverkko CLI +will detect new task approximately within one second after file is +saved and will start processing it. + +If your text editor automatically reloads file when it was changed by +other process in the filesystem, AI response will appear within text +editor as soon as AI response is ready. If needed, you can add further +queries at the end of the file and re-add "TOCOMPUTE:" at the +beginning of the file. This way AI will process file again and file +becomes stateful conversation. If you use GNU Emacs text editor, you +can benefit from [[id:25038854-c905-4b26-9670-cca06600223e][purpose-built GNU Emacs utilities]]. + +** Helpful GNU Emacs utilities +:PROPERTIES: +:ID: 25038854-c905-4b26-9670-cca06600223e +:END: + +Note: GNU Emacs and following Emacs Lisp utilities are not required to +use Älyverkko CLI. Their purpose is to increase comfort for existing +GNU Emacs users. + +*** Easily compose new problem statement for AI from emacs + +The Elisp function *ai-new-topic* facilitates the creation and opening +of a new Org-mode file dedicated to a user-defined topic within a +specified directory. Now you can use this file within emacs to compose +you problem statement to AI. + +When *ai-new-topic* function triggered, it first prompts the user to +input a topic name. This name will serve as the basis for the filename +and the title within the document. + +The function then constructs a file path by concatenating the +pre-defined =alyverkko-topic-files-directory= (which should be set to +your topics directory), the topic name, and the =.org= extension. If a +file with this path does not already exist, the function will create a +new file and open it for editing. + +#+begin_src elisp :results none + + (defvar alyverkko-task-files-directory "/home/user/my-ai-tasks-directory/" + "Directory where task files are stored.") + + (defun alyverkko-new-task () + "Create and open a task file in the specified directory." + (interactive) + (let ((task (read-string "Enter task name: "))) + (let ((file-path (concat alyverkko-task-files-directory task ".org"))) + (if (not (file-exists-p file-path)) + (with-temp-file file-path + )) + (find-file file-path) + (goto-char (point-max)) + (org-mode)))) + +#+end_src + +*** Easily signal to AI that problem statement is ready for solving + +The function =alyverkko-compute= is designed to enhance the workflow +of users working with the Älyverkko CLI application by automating the +process of marking text files for computation with a specific AI model +and prompt. + +When function is invoked, it detects available prompts and allows user +to select one. + +Thereafter function detects available models from Älyverkko CLI +configuration file and allows user to select one. + +Finally function inserts at the beginning of currently opened file +something like this: + +: TOCOMPUTE: prompt= model= priority= + +- Adjust *prompt-dir* variable to point to your prompts directory. +- Adjust *config-file* variable to point to your Älyverkko CLI + configuration file path. + +#+begin_src emacs-lisp + +(defun alyverkko-compute () + "Interactively pick a skill, model, and priority, then insert a +TOCOMPUTE line at the top of the current buffer. + +Adjust `skill-dir` and `config-file` to match your setup." + (interactive) + (let ((skill-dir "~/.config/alyverkko-cli/skills/") + (config-file "~/.config/alyverkko-cli/alyverkko-cli.yaml") + models) + + ;; Harvest model aliases from the Älyverkko CLI config + (with-temp-buffer + (insert-file-contents config-file) + (goto-char (point-min)) + (when (search-forward-regexp "^models:" nil t) + (while (search-forward-regexp "^\\s-+- alias: \"\\([^\"]+\\)\"" nil t) + (push (match-string 1) models)))) + + (if (file-exists-p skill-dir) + (let* ((files (directory-files skill-dir t "\\`[^.].*\\.yaml\\'")) + (aliases (mapcar #'file-name-base files))) + (if aliases + (let* ((selected-alias (completing-read "Select skill: " aliases)) + (model (completing-read "Select AI model: " models)) + (priority (number-to-string + (read-number "Priority (integer, default 0): " 0)))) + (alyverkko-insert-tocompute-line selected-alias model priority)) + (message "No skill files found."))) + (message "Skill directory not found.")))) + +(defun alyverkko-insert-tocompute-line (skill-alias model &optional priority) + "Insert a TOCOMPUTE line with SKILL-ALIAS, MODEL, and PRIORITY at +the top of the current buffer." + (save-excursion + (goto-char (point-min)) + (insert (format "TOCOMPUTE: skill=%s model=%s priority=%s\n" + skill-alias model (or priority "0"))) + (save-buffer))) + +#+end_src + +* Getting the source code +- This program is free software: released under Creative Commons Zero + (CC0) license. + +- Program author: + - Svjatoslav Agejenko + - Homepage: https://svjatoslav.eu + - Email: mailto://svjatoslav@svjatoslav.eu + +- [[https://www.svjatoslav.eu/projects/][Other software projects hosted at svjatoslav.eu]] + +** Source code +:PROPERTIES: +:ID: f5740953-079b-40f4-87d8-b6d1635a8d39 +:END: +- [[https://www2.svjatoslav.eu/gitweb/?p=alyverkko-cli.git;a=snapshot;h=HEAD;sf=tgz][Download latest snapshot in TAR GZ format]] + +- [[https://www2.svjatoslav.eu/gitweb/?p=alyverkko-cli.git;a=summary][Browse Git repository online]] + +- Clone Git repository using command: + : git clone https://www3.svjatoslav.eu/git/alyverkko-cli.git + +- See [[https://www3.svjatoslav.eu/projects/alyverkko-cli/apidocs/][JavaDoc]]. + +- See [[https://www3.svjatoslav.eu/projects/alyverkko-cli/graphs/][automatically generated class diagrams]]. Here is [[https://www3.svjatoslav.eu/projects/javainspect/legend.png][legend]] to help + understand diagrams. Diagrams were generated using [[https://www3.svjatoslav.eu/projects/javainspect/][JavaInspect tool]]. + +* Feature ideas + +- In task directory ignore binary files, use joinfiles command as + example how to ignore binary files. Perhaps extract plain text file + detection into some utility class. + +- Make text editor configurable in application properties file. + +- Extend the application's capabilities to include voice capture and + processing. + +- Implement image capture and processing features. diff --git a/doc/pausing and resuming.pdf b/doc/pausing and resuming.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a1a2be28985c070b8e0c367014298f1cd0480565 GIT binary patch literal 196957 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 literal 0 HcmV?d00001 diff --git a/install b/install new file mode 100755 index 0000000..9439a1e --- /dev/null +++ b/install @@ -0,0 +1,96 @@ +#!/bin/bash + +SYSTEMD_SERVICE_FILE="/etc/systemd/system/alyverkko-cli.service" + + +# Function to install binary and jar +install_to_opt() { + sudo rm -rf /opt/alyverkko-cli/ + sudo mkdir -p /opt/alyverkko-cli/ + sudo chmod 755 /opt/alyverkko-cli/ + + sudo cp target/alyverkko-cli.jar "/opt/alyverkko-cli/alyverkko-cli.jar" + sudo cp "alyverkko-cli" "/opt/alyverkko-cli/alyverkko-cli" + sudo chmod +x "/opt/alyverkko-cli/alyverkko-cli" + sudo cp logo.png "/opt/alyverkko-cli/logo.png" + + sudo ln -sf "/opt/alyverkko-cli/alyverkko-cli" /usr/bin/alyverkko-cli +} + +# Function to install the desktop launcher +install_desktop_entry() { + sudo cp launchers/alyverkko-cli*.desktop /usr/share/applications/ + sudo chmod 644 /usr/share/applications/alyverkko-cli*.desktop +} + +# Function to install systemd service +install_systemd_service() { + + cat < /dev/null +[Unit] +Description=Älyverkko CLI daemon in task processor mode +After=network.target + +[Service] +User=$USER +ExecStart=/opt/alyverkko-cli/alyverkko-cli process +WorkingDirectory=/opt/alyverkko-cli +Nice=10 +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +EOF + + sudo systemctl daemon-reload + sudo systemctl enable alyverkko-cli + sudo systemctl start alyverkko-cli + sleep 1 + echo "Systemd service installed, enabled and started. Service status is:" + systemctl --no-pager -l status alyverkko-cli +} + + +# Function to pre-deploy example configuration YAML file +install_config_file() { + local alyverkko_config_dir="${HOME}/.config/alyverkko-cli" + + if [ ! -d "$alyverkko_config_dir" ]; then + mkdir -p "$alyverkko_config_dir" + cp alyverkko-cli.yaml "$alyverkko_config_dir/" + else + echo "Configuration directory already exists: $alyverkko_config_dir" + fi +} + +# Main installation function +main() { + # Build the application + mvn --settings maven.xml clean package + + install_to_opt + install_desktop_entry + install_config_file + + # Check if systemd service already exists + if [ -f "$SYSTEMD_SERVICE_FILE" ]; then + echo "Systemd service is already installed." + # Display the status without hanging + echo "Service status is:" + systemctl --no-pager -l status alyverkko-cli + else + # Install systemd service if requested + echo "Do you want to install Älyverkko CLI as a systemd service? (y/N)" + read install_service + + if [[ $install_service == [Yy] ]]; then + install_systemd_service + fi + fi + + echo "Installation complete." +} + +# Call the main installation function +main diff --git a/launchers/alyverkko-cli-pause.desktop b/launchers/alyverkko-cli-pause.desktop new file mode 100644 index 0000000..ae51de9 --- /dev/null +++ b/launchers/alyverkko-cli-pause.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=Pause Älyverkko CLI +Comment=Freeze the current llama-cli job +Icon=media-playback-pause +Exec=/usr/bin/killall -STOP llama-cli +Terminal=false +Categories=Development; diff --git a/launchers/alyverkko-cli-resume.desktop b/launchers/alyverkko-cli-resume.desktop new file mode 100644 index 0000000..029fad7 --- /dev/null +++ b/launchers/alyverkko-cli-resume.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=Resume Älyverkko CLI +Comment=Unfreeze the current llama-cli job +Icon=media-playback-pause +Exec=/usr/bin/killall -CONT llama-cli +Terminal=false +Categories=Development; diff --git a/launchers/alyverkko-cli.desktop b/launchers/alyverkko-cli.desktop new file mode 100644 index 0000000..54378ee --- /dev/null +++ b/launchers/alyverkko-cli.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Terminal=true +Name=Älyverkko CLI +Comment=Runner for artificial neural network service +Icon=/opt/alyverkko-cli/logo.png +Exec=/opt/alyverkko-cli/alyverkko-cli process +Categories=Development; diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..468758e81c57f304665f959dcd47446445a7f026 GIT binary patch literal 4012 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 literal 0 HcmV?d00001 diff --git a/maven.xml b/maven.xml new file mode 100644 index 0000000..505327a --- /dev/null +++ b/maven.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..624e71c --- /dev/null +++ b/pom.xml @@ -0,0 +1,209 @@ + + 4.0.0 + eu.svjatoslav + alyverkko-cli + 1.0-SNAPSHOT + Älyverkko CLI + AI engine wrapper + + + UTF-8 + UTF-8 + + + + svjatoslav.eu + https://svjatoslav.eu + + + + + eu.svjatoslav + svjatoslavcommons + 1.8 + + + eu.svjatoslav + cli-helper + 1.3 + + + org.testng + testng + 7.7.0 + test + + + org.junit.jupiter + junit-jupiter + RELEASE + test + + + com.fasterxml.jackson.core + jackson-databind + 2.13.4.1 + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + 2.13.0 + + + org.apache.commons + commons-lang3 + 3.12.0 + + + org.apache.commons + commons-io + 1.3.2 + + + org.projectlombok + lombok + 1.18.32 + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 11 + 11 + true + UTF-8 + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + attach-javadocs + + jar + + + + + + + + foo + bar + + + + ${java.home}/bin/javadoc + + + + + org.apache.maven.plugins + maven-resources-plugin + 2.4.3 + + UTF-8 + + + + + maven-assembly-plugin + + + + + eu.svjatoslav.alyverkko_cli.Main + + + + jar-with-dependencies + + alyverkko-cli + false + + + + + package-jar-with-dependencies + package + + single + + + + jar-with-dependencies + + + + eu.svjatoslav.alyverkko_cli.Main + + + + + + + + + + + org.apache.maven.wagon + wagon-ssh-external + 2.6 + + + + + + + + svjatoslav.eu + svjatoslav.eu + scpexe://svjatoslav.eu:10006/srv/maven + + + svjatoslav.eu + svjatoslav.eu + scpexe://svjatoslav.eu:10006/srv/maven + + + + + + svjatoslav.eu + Svjatoslav repository + https://www3.svjatoslav.eu/maven/ + + + + + scm:git:ssh://n0@svjatoslav.eu/home/git/repositories/alyverkko-cli.git + scm:git:ssh://n0@svjatoslav.eu/home/git/repositories/alyverkko-cli.git + + + + + diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/Command.java b/src/main/java/eu/svjatoslav/alyverkko_cli/Command.java new file mode 100644 index 0000000..01b48b3 --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/Command.java @@ -0,0 +1,32 @@ +package eu.svjatoslav.alyverkko_cli; + +import java.io.IOException; + +/** + *

Base interface for all subcommands in the Älyverkko CLI. Each command must define its name and execution logic. + *

Commands typically: + *

    + *
  • Parse their own specific arguments
  • + *
  • Access the global configuration
  • + *
  • Handle I/O operations
  • + *
+ * + *

Commands should be stateless and self-contained, using the configuration object for persistent data when needed. + */ +public interface Command { + + /** + * @return the subcommand's name. + */ + String getCommandName(); + + /** + * Called to carry out the specific subcommand. Typically, reads + * command-line arguments and performs the desired action. + * + * @param args arguments passed after the subcommand name. + * @throws IOException if I/O operations fail. + * @throws InterruptedException if the operation is interrupted. + */ + void executeCommand(String[] args) throws IOException, InterruptedException; +} diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/Main.java b/src/main/java/eu/svjatoslav/alyverkko_cli/Main.java new file mode 100644 index 0000000..289ede4 --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/Main.java @@ -0,0 +1,83 @@ +package eu.svjatoslav.alyverkko_cli; + +import eu.svjatoslav.alyverkko_cli.commands.*; +import eu.svjatoslav.alyverkko_cli.commands.task_processor.TaskProcessorCommand; +import eu.svjatoslav.alyverkko_cli.configuration.Configuration; + +import java.io.IOException; +import java.util.Optional; + +import static java.util.Arrays.copyOfRange; + +/** + * The main entry point for the Älyverkko CLI application. + * It processes subcommands such as "wizard", "selftest", "joinfiles", + * "mail", and "listmodels". + */ +public class Main { + + /** + * The list of all supported subcommands. + */ + private final java.util.List commands = java.util.Arrays.asList( + new ListModelsCommand(), + new TaskProcessorCommand(), + new JoinFilesCommand(), + new WizardCommand() + ); + + /** + * The active, loaded configuration for the entire application. + * May be null if the configuration is not loaded properly. + */ + public static Configuration configuration; + + /** + * Application entry point. Dispatches to a subcommand if one is + * specified; otherwise shows usage help. + * + * @param args command-line arguments; the first is the subcommand name. + */ + public static void main(final String[] args) throws IOException, InterruptedException { + new Main().handleCommand(args); + } + + /** + * Attempts to find and execute the subcommand specified in the given arguments, + * or prints a help message if no command is found. + * + * @param args the command-line arguments. + * @throws IOException if an I/O error occurs during command execution. + * @throws InterruptedException if the command is interrupted. + */ + public void handleCommand(String[] args) throws IOException, InterruptedException { + if (args.length == 0) { + showHelp(); + return; + } + + String commandName = args[0].toLowerCase(); + Optional commandOptional = commands.stream() + .filter(cmd -> cmd.getCommandName().equals(commandName)) + .findFirst(); + + if (!commandOptional.isPresent()) { + System.out.println("Unknown command: " + commandName); + showHelp(); + return; + } + + Command command = commandOptional.get(); + String[] remainingArgs = copyOfRange(args, 1, args.length); + command.executeCommand(remainingArgs); + } + + /** + * Displays a basic help message, listing available commands. + */ + private void showHelp() { + System.out.println("Älyverkko CLI\n"); + System.out.println("Available commands:"); + commands.forEach(cmd -> System.out.println(" " + cmd.getCommandName())); + } +} diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/Utils.java b/src/main/java/eu/svjatoslav/alyverkko_cli/Utils.java new file mode 100644 index 0000000..87e8531 --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/Utils.java @@ -0,0 +1,23 @@ +package eu.svjatoslav.alyverkko_cli; + +/** + *

General utility functions for the Älyverkko CLI application. Currently provides ANSI color output capabilities for + * console messages. + *

Color formatting follows standard ANSI escape sequences, with specific methods for common message types like errors. + *

For future extensions, this class could include additional helper functions for file operations or string processing. + */ +public class Utils { + + /** + * Prints a message in red text to the console. + * + * @param message the text to print in red. + */ + public static void printRedMessageToConsole(String message) { + // set output color to red + System.out.print("\033[0;31m"); + System.out.print(message + "\n"); + // reset output color + System.out.print("\033[0m"); + } +} diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/commands/JoinFilesCommand.java b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/JoinFilesCommand.java new file mode 100644 index 0000000..aa0ff69 --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/JoinFilesCommand.java @@ -0,0 +1,215 @@ +package eu.svjatoslav.alyverkko_cli.commands; + +import eu.svjatoslav.alyverkko_cli.Command; +import eu.svjatoslav.commons.cli_helper.parameter_parser.Parser; +import eu.svjatoslav.commons.cli_helper.parameter_parser.parameter.DirectoryOption; +import eu.svjatoslav.commons.cli_helper.parameter_parser.parameter.NullOption; +import eu.svjatoslav.commons.cli_helper.parameter_parser.parameter.StringOption; +import eu.svjatoslav.commons.string.GlobMatcher; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.*; + +import static eu.svjatoslav.alyverkko_cli.Main.configuration; +import static eu.svjatoslav.alyverkko_cli.configuration.ConfigurationHelper.getConfigurationFile; +import static eu.svjatoslav.alyverkko_cli.configuration.ConfigurationHelper.loadConfiguration; + +/** + * The JoinFilesCommand aggregates multiple files (optionally matching + * a specific pattern) into a single file for AI processing, typically + * in the mail directory. + * + * Usage Example: + *

+ *   alyverkko-cli joinfiles -s /path/to/source -p "*.java" -t "my_topic" --edit
+ * 
+ */ + +public class JoinFilesCommand implements Command { + + /** + * A command-line parser to handle joinfiles arguments. + */ + final Parser parser = new Parser(); + + /** + * Directory from which files will be joined. + */ + public DirectoryOption sourceDirectoryOption = parser.add(new DirectoryOption("Directory to join files from")) + .addAliases("--src-dir", "-s") + .mustExist(); + + /** + * Pattern for matching files, such as "*.java". + */ + public StringOption patternOption = parser.add(new StringOption("Pattern to match files")) + .addAliases("--pattern", "-p"); + + /** + * Topic name, used as the basis for the output file name. + */ + public StringOption topic = parser.add(new StringOption("Topic of the joined files")) + .addAliases("--topic", "-t") + .setMandatory(); + + /** + * If present, open the joined file using a text editor afterward. + */ + public NullOption editOption = parser.add(new NullOption("Edit the joined file using text editor")) + .addAliases("--edit", "-e"); + + /** + * The base directory for recursion when joining files. + */ + public Path sourceBaseDirectory; + + /** + * The pattern used to filter files for joining, e.g. "*.java". + */ + public String fileNamePattern = null; + + /** + * The resulting output file that aggregates all matched files. + */ + File outputFile; + + /** + * @return the name of this command, i.e., "joinfiles". + */ + @Override + public String getCommandName() { + return "joinfiles"; + } + + /** + * Executes the command that joins files from a specified directory + * (matching an optional pattern) into one output file in the mail + * directory. Optionally, it can open the output file in an editor. + * + * @param cliArguments the command-line arguments after "joinfiles". + * @throws IOException if any IO operations fail. + */ + @Override + public void executeCommand(String[] cliArguments) throws IOException { + configuration = loadConfiguration(getConfigurationFile(null)); + if (configuration == null){ + System.out.println("Failed to load configuration file"); + return; + } + + if (!parser.parse(cliArguments)) { + System.out.println("Failed to parse command-line arguments"); + parser.showHelp(); + return; + } + + // Build the path to the target file that is relative to the mail directory + outputFile = configuration.getTaskDirectory().toPath().resolve(topic.getValue() + ".org").toFile(); + + if (patternOption.isPresent()) { + fileNamePattern = patternOption.getValue(); + joinFiles(); + } + + if (editOption.isPresent()) { + openFileWithEditor(); + } + } + + /** + * Opens the joined file with a text editor. Currently uses a + * command "emc" as an example—adapt as needed. + * + * @throws IOException if the launch of the editor fails. + */ + private void openFileWithEditor() throws IOException { + String[] cmd = {"emc", outputFile.getAbsolutePath()}; + Runtime.getRuntime().exec(cmd); + } + + /** + * Joins the matching files from the configured source directory + * into a single file named {@code .org} in the mail directory. + * + * @throws IOException if reading or writing files fails. + */ + private void joinFiles() throws IOException { + boolean appendToFile = outputFile.exists(); + + if (sourceDirectoryOption.isPresent()) { + sourceBaseDirectory = sourceDirectoryOption.getValue().toPath(); + } else { + sourceBaseDirectory = Paths.get("."); + } + + try (BufferedWriter writer = Files.newBufferedWriter( + outputFile.toPath(), StandardCharsets.UTF_8, + appendToFile ? StandardOpenOption.APPEND : StandardOpenOption.CREATE)) { + + // Recursively join files that match the pattern + joinFilesRecursively(sourceBaseDirectory, writer); + } + + System.out.println("Files have been joined into: " + outputFile.getAbsolutePath()); + } + + /** + * Recursively traverses the specified directory and writes the contents + * of files that match the specified {@link #fileNamePattern}. + * + * @param directoryToIndex the directory to be searched recursively. + * @param writer the writer to which file contents are appended. + * @throws IOException if file reading fails. + */ + private void joinFilesRecursively(Path directoryToIndex, BufferedWriter writer) throws IOException { + try (DirectoryStream stream = Files.newDirectoryStream(directoryToIndex)) { + for (Path entry : stream) { + if (Files.isDirectory(entry)) { + joinFilesRecursively(entry, writer); + } else if (Files.isRegularFile(entry)) { + String fileName = entry.getFileName().toString(); + + boolean match = GlobMatcher.match(fileName, fileNamePattern); + if (match) { + System.out.println("Joining file: " + fileName); + writeFile(writer, entry); + } + } + } + } + } + + /** + * Writes the contents of a single file to the specified writer, + * including a small header containing the file path. + * + * @param writer the writer to which file contents are appended. + * @param entry the file to read and write. + * @throws IOException if file reading or writing fails. + */ + private void writeFile(BufferedWriter writer, Path entry) throws IOException { + writeFileHeader(writer, entry); + + String fileContent = new String(Files.readAllBytes(entry), StandardCharsets.UTF_8); + + // remove empty lines from the beginning and end of the file + fileContent = fileContent.replaceAll("(?m)^\\s*$", ""); + + writer.write(fileContent + "\n"); + } + + /** + * Writes a small header line to indicate which file is being appended. + * + * @param writer the writer to which the header is appended. + * @param entry the path of the current file. + * @throws IOException if writing fails. + */ + private void writeFileHeader(BufferedWriter writer, Path entry) throws IOException { + String relativePath = sourceBaseDirectory.relativize(entry).toString(); + writer.write("* file: " + relativePath + "\n\n"); + } +} diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/commands/ListModelsCommand.java b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/ListModelsCommand.java new file mode 100644 index 0000000..2ad1d7c --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/ListModelsCommand.java @@ -0,0 +1,53 @@ +package eu.svjatoslav.alyverkko_cli.commands; + +import eu.svjatoslav.alyverkko_cli.Command; +import eu.svjatoslav.alyverkko_cli.model.ModelLibrary; + +import java.io.IOException; + +import static eu.svjatoslav.alyverkko_cli.Main.configuration; +import static eu.svjatoslav.alyverkko_cli.configuration.ConfigurationHelper.getConfigurationFile; +import static eu.svjatoslav.alyverkko_cli.configuration.ConfigurationHelper.loadConfiguration; + +/** + *

Displays all available AI models in the configured models directory. This command provides a quick overview of + * currently available models and their metadata. + *

The implementation: + *

    + *
  • Loads the configuration
  • + *
  • Instantiates ModelLibrary
  • + *
  • Prints model details using ModelLibrary's printModels()
  • + *
+ * + *

This command is primarily intended for administrative use to verify model availability before running tasks. + */ +public class ListModelsCommand implements Command { + + /** + * @return the name of this command, i.e., "listmodels". + */ + @Override + public String getCommandName() { + return "listmodels"; + } + + /** + * Executes the command to load the user's configuration and list + * all known AI models, printing them to stdout. + * + * @param cliArguments the command-line arguments after "listmodels". + * @throws IOException if loading configuration fails. + */ + @Override + public void executeCommand(String[] cliArguments) throws IOException { + configuration = loadConfiguration(getConfigurationFile(null)); + if (configuration == null){ + System.out.println("Failed to load configuration file"); + return; + } + + System.out.println("Listing models in directory: " + configuration.getModelsDirectory()); + ModelLibrary modelLibrary = new ModelLibrary(configuration.getModelsDirectory(), configuration.getModels()); + modelLibrary.printModels(); + } +} diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/commands/WizardCommand.java b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/WizardCommand.java new file mode 100644 index 0000000..7808a62 --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/WizardCommand.java @@ -0,0 +1,482 @@ +package eu.svjatoslav.alyverkko_cli.commands; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import eu.svjatoslav.alyverkko_cli.Command; +import eu.svjatoslav.alyverkko_cli.configuration.Configuration; +import eu.svjatoslav.alyverkko_cli.configuration.ConfigurationHelper; +import eu.svjatoslav.alyverkko_cli.configuration.ConfigurationModel; +import eu.svjatoslav.commons.cli_helper.parameter_parser.Parser; +import eu.svjatoslav.commons.cli_helper.parameter_parser.parameter.FileOption; + +import java.io.*; +import java.nio.file.*; +import java.util.ArrayList; +import java.util.List; + +import static eu.svjatoslav.alyverkko_cli.Utils.printRedMessageToConsole; +import static eu.svjatoslav.alyverkko_cli.configuration.ConfigurationHelper.getConfigurationFile; +import static eu.svjatoslav.commons.cli_helper.CLIHelper.*; + +/** + *

Interactive configuration wizard that helps users validate and fix their configuration files. + * It performs system checks and offers to fix any missing or invalid paths, discovers new models, + * and updates the configuration accordingly. + *

Key workflow steps: + *

    + *
  1. Load or create configuration
  2. + *
  3. Validate core directory paths
  4. + *
  5. Discover and annotate new models
  6. + *
  7. Save updated configuration
  8. + *
+ *

When handling split models (.gguf files with part numbering), the wizard automatically + * detects base models and only adds part-1 files to the configuration. + */ +public class WizardCommand implements Command { + + // Command-line parser to handle wizard arguments + private final Parser cliParser = new Parser(); + + /** + * Optional CLI argument for specifying a configuration file path. + */ + public FileOption configFileOption = cliParser.add(new FileOption("Configuration file path")) + .addAliases("--config", "-c"); + + /** + * The configuration object (loaded or newly created) + */ + private Configuration configuration; + + private File configurationFile; + + private boolean configurationUpdated = false; + private boolean modelsUpdated = false; + + @Override + public String getCommandName() { + return "wizard"; + } + + @Override + public void executeCommand(String[] cliArguments) throws IOException { + // Parse command-line arguments + if (!cliParser.parse(cliArguments)) { + System.out.println("Failed to parse command-line arguments"); + cliParser.showHelp(); + return; + } + + configurationFile = getConfigurationFile(configFileOption); + loadOrCreateConfiguration(); + + checkAndFixGeneralParameters(); + + fixModelEntries(); + + trySaveConfiguration(); + + if (modelsUpdated) { + System.out.println("Configuration has been updated. Please open the configuration file in a text editor to review and adjust model settings as needed."); + } + } + + private void loadOrCreateConfiguration() throws IOException { + validateConfigurationFile(); + + if (configurationFile.exists()) { + System.out.println("Found existing configuration at: \"" + configurationFile.getAbsolutePath() + "\""); + configuration = ConfigurationHelper.loadConfiguration(configurationFile); + } else { + // If no config found, create a fresh one + System.out.println("Existing configuration not found at \"" + + configurationFile.getAbsolutePath() + "\". Initializing new blank configuration."); + configuration = new Configuration(); + configurationUpdated = true; + } + } + + /** + * Validates the configuration file path and checks if it is a valid file. + * If not, prints an error message and exits the program. + */ + private void validateConfigurationFile() { + if (!configurationFile.exists()) return; // No need to check further if it doesn't exist + + // Check if the file is a directory + if (configurationFile.isDirectory()) { + System.err.println("ERROR: Configuration file path incorrectly points to a directory: \"" + configurationFile.getAbsolutePath() + + "\". Please specify a file instead."); + System.exit(1); + } + + // Check if the file is readable + if (!configurationFile.canRead()) { + System.err.println("ERROR: Cannot read configuration file: \"" + configurationFile.getAbsolutePath() + + "\". Please check permissions."); + System.exit(1); + } + + // Check if the file is writable + if (!configurationFile.canWrite()) { + System.err.println("ERROR: Cannot write to configuration file: \"" + configurationFile.getAbsolutePath() + + "\". Please check file permissions."); + System.exit(1); + } + } + + /** + * Step-by-step checking (and possibly fixing) of each main config parameter. + */ + private void checkAndFixGeneralParameters() { + configuration.setTaskDirectory( + checkDirectory( + configuration.getTaskDirectory(), + "Mail directory", + true, + "The mail directory is where the AI will look for tasks to solve. " + + "It should be a directory that you can write to. Please specify new mail directory path.", + true) + ); + + configuration.setModelsDirectory( + checkDirectory( + configuration.getModelsDirectory(), + "Models directory", + null, + "The models directory is where the AI model files (*.gguf) are stored. " + + "Please specify new models directory path.", + true) + ); + + configuration.setSkillsDirectory( + checkDirectory( + configuration.getSkillsDirectory(), + "Prompts directory", + null, + "The prompts directory is where the AI prompt files (*.yaml) are stored. " + + "Please specify new prompts directory path.", + true) + ); + + configuration.setLlamaCliPath( + checkFile( + configuration.getLlamaCliPath(), + "llama.cpp project llama-cli executable file path", + "The llama-cli is commandline engine that runs GGUF language models. " + + "Usually it is located under build/bin/ directory within llama.cpp project.", + true, + true) + ); + + // Default_temperature + Float temperature = configuration.getDefaultTemperature(); + if (temperature == null || temperature < 0f || temperature > 3f) { + configuration.setDefaultTemperature(askFloat( + "Enter default temperature (0-3). Lower => more deterministic, higher => more creative.", + temperature, + 0f, 3f, false + )); + } + // Thread_count + Integer threadCount = configuration.getThreadCount(); + if (threadCount == null || threadCount < 1) { + int defaultThreadCount = Runtime.getRuntime().availableProcessors() / 2; + if (defaultThreadCount < 1) defaultThreadCount = 1; + configuration.setThreadCount(askInteger( + "Enter number of CPU threads for AI generation. Typically RAM bandwidth gets saturated " + + "first and becomes bottleneck before all CPU cores can get fully utilized. So for 12 core CPU" + + " it might be enough to set 6 threads. Increasing this number higher yields diminishing returns.", + defaultThreadCount, + 1, null, false + )); + } + + // Batch thread count + Integer batchThreadCount = configuration.getBatchThreadCount(); + if (batchThreadCount == null || batchThreadCount < 1) { + int defaultThreadCount = Runtime.getRuntime().availableProcessors(); + configuration.setBatchThreadCount( + askInteger( + "\nEnter number of CPU threads for input prompt processing (all cores is often fine).", + defaultThreadCount, + 1, null, false + )); + } + } + + /** + * Validates a directory path and prompts user to fix if needed. + * @param directory current directory value + * @param directoryName name to display to user + * @param writable if directory must be writable (null = no check) + * @param explanation message to show user when prompting + * @param offerToCreate if true, offers to create directory if missing + * @return validated directory path + */ + private File checkDirectory( + File directory, + String directoryName, + Boolean writable, + String explanation, + boolean offerToCreate) { + + while (true) { + // Check if the directory is null + boolean allOk = true; + if (directory == null) { + System.out.println(directoryName + " is not defined."); + allOk = false; + directory = getNewDirectoryPath(explanation, writable); + } + + if (!directory.exists()) { + System.out.println(directoryName + " does not exist: " + directory.getAbsolutePath()); + allOk = false; + // Offer to create it + if (offerToCreate) { + if (askBoolean("Create " + directoryName + " ?", true)) { + boolean created = directory.mkdirs(); + if (!created) { + printRedMessageToConsole("Failed to create \"" + directory + "\". Check permissions"); + } else { + System.out.println(directoryName + " created at: " + directory.getAbsolutePath()); + } + } + } + } + + if (!directory.isDirectory()) { + System.out.println(directoryName + " is not a directory: " + directory.getAbsolutePath()); + allOk = false; + directory = getNewDirectoryPath(explanation, writable); + } + + if (writable != null && writable && !directory.canWrite()) { + System.out.println(directoryName + " is not writable: " + directory.getAbsolutePath()); + allOk = false; + directory = getNewDirectoryPath(explanation, writable); + } + + if (allOk) { + System.out.println(directoryName + " is: " + directory.getAbsolutePath()); + return directory; + } + + configurationUpdated = true; + } + } + + /** + * Validates a file path and prompts user to fix if needed. + * @param file current file value + * @param fileName name to display to user + * @param mustExist if file must exist (null = no check) + * @param explanation message to show user when prompting + * @param executable if file must be executable (null = no check) + * @return validated file path + */ + private File checkFile( + File file, + String fileName, + String explanation, + Boolean mustExist, + Boolean executable) { + + while (true) { + boolean allOk = true; + if (file == null) { + System.out.println(fileName + " is not defined."); + allOk = false; + file = askFile(explanation, null, mustExist, null, null, executable, false); + } + + if (mustExist != null && mustExist && !file.exists()) { + System.out.println(fileName + " does not exist: " + file.getAbsolutePath()); + allOk = false; + file = askFile(explanation, null, mustExist, null, null, executable, false); + } + + if (!file.isFile()) { + System.out.println(fileName + " is not a file: " + file.getAbsolutePath()); + allOk = false; + file = askFile(explanation, null, null, null, null, executable, false); + } + + if (executable != null && executable && !file.canExecute()) { + System.out.println(fileName + " is not executable: " + file.getAbsolutePath()); + allOk = false; + file = askFile(explanation, null, true, null, null, executable, false); + } + + if (allOk) { + System.out.println(fileName + " is: " + file.getAbsolutePath()); + return file; + } + + configurationUpdated = true; + } + } + + private static File getNewDirectoryPath(String directoryName, Boolean writable) { + return askDirectory(directoryName, null, null, null, writable, null, false); + } + + /** + * Saves the config to the default path, verifying if user wants to + * overwrite if it already exists, etc. + */ + private void trySaveConfiguration() { + if (!configurationUpdated) { + System.out.println("No changes made to the configuration. Not saving."); + return; + } + + // ask user if user wants to save configuration + if (!askBoolean("Save configuration to: " + configurationFile, true, false)) + return; + + boolean fileExisted = configurationFile.exists(); + + try { + Files.createDirectories(configurationFile.toPath().getParent()); + try (BufferedWriter writer = Files.newBufferedWriter( + configurationFile.toPath(), + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING + )) { + new ObjectMapper(new YAMLFactory()).writeValue(writer, configuration); + } + if (fileExisted) { + System.out.println("Existing configuration updated at: " + configurationFile.toPath()); + } else { + System.out.println("New configuration created at: " + configurationFile.toPath()); + } + } catch (IOException e) { + printRedMessageToConsole("Error saving configuration: " + e.getMessage()); + } + } + + /** + * Generates an alias from a .gguf filename by removing non-alphanumeric chars. + */ + private String suggestAlias(String filePath) { + String fileName = new File(filePath).getName(); + // Remove .gguf extension + fileName = fileName.replaceFirst("\\.gguf$", ""); + // Check if it's a split model + if (fileName.matches(".*-\\d{5}-of-\\d{5}")) { + // Extract base name by removing the part numbers + fileName = fileName.replaceFirst("-\\d{5}-of-\\d{5}", ""); + } + // Replace non-alphanumeric characters with hyphens + String alias = fileName.replaceAll("[^a-zA-Z0-9]", "-").toLowerCase(); + // Normalize hyphens and trim leading/trailing hyphens + return alias.replaceAll("-+", "-").replaceAll("^-|-$", ""); + } + + private void fixModelEntries() { + File modelsDir = configuration.getModelsDirectory(); + if (modelsDir == null || !modelsDir.exists() || !modelsDir.isDirectory()) { + System.out.println("Models directory is not properly configured. Skipping model checks."); + return; + } + + List existingModels = configuration.getModels(); + if (existingModels == null) { + existingModels = new ArrayList<>(); + configuration.setModels(existingModels); + configurationUpdated = true; + } + + annotateMissingModels(); + + discoverNewModels(); + } + + private void discoverNewModels() { + // List all .gguf files in models directory + File[] files = configuration.getModelsDirectory().listFiles((dir, name) -> name.endsWith(".gguf")); + if (files == null) return; + + for (File file : files) { + String relativePath = configuration.getModelsDirectory().toPath().relativize(file.toPath()).toString(); + if (isExistingModel(relativePath)) continue; + + processPotentialNewModelFile(file, relativePath); + } + } + + private boolean isExistingModel(String relativePath) { + return configuration.getModels().stream() + .anyMatch(m -> m.getFilesystemPath().equals(relativePath)); + } + + private void processPotentialNewModelFile(File file, String relativePath) { + // Check if it's a split model + if (isSplitModel(file.getName())) { + handleSplitModel(file, relativePath); + } else { + addNewModel(relativePath); + } + } + + private boolean isSplitModel(String fileName) { + return fileName.matches(".*-\\d{5}-of-\\d{5}\\.gguf"); + } + + private void handleSplitModel(File file, String relativePath) { + String baseName = relativePath.replaceFirst("-\\d{5}-of-\\d{5}\\.gguf", ""); + if (configuration.getModels().stream().anyMatch(m -> m.getAlias().startsWith(baseName))) { + return; + } + + // Extract part number + String partNumberStr = relativePath.replaceAll(".*-(\\d{5}-of-\\d{5}\\.gguf)", "$1"); + int partNumber = Integer.parseInt(partNumberStr.split("-of-")[0]); + if (partNumber == 1) { + addNewModel(relativePath); + } + } + + private void addNewModel(String relativePath) { + ConfigurationModel newModel = getNewModel(relativePath); + configuration.getModels().add(newModel); + System.out.println("Added new model: " + newModel.getAlias() + " (" + newModel.getFilesystemPath() + ")"); + configurationUpdated = true; + modelsUpdated = true; + } + + private ConfigurationModel getNewModel(String relativePath) { + String suggestedAlias = suggestAlias(relativePath); + ConfigurationModel newModel = new ConfigurationModel(); + newModel.setAlias(suggestedAlias + "-new"); + newModel.setFilesystemPath(relativePath); + newModel.setContextSizeTokens(32768); // Default context size + newModel.setEndOfTextMarker(null); // Default end marker + return newModel; + } + + private void annotateMissingModels() { + // Process existing models to add/remove -missing suffix + for (ConfigurationModel model : configuration.getModels()) { + File modelFile = new File(configuration.getModelsDirectory(), model.getFilesystemPath()); + if (!modelFile.exists()) { + if (!model.getAlias().endsWith("-missing")) { + model.setAlias(model.getAlias() + "-missing"); + System.out.println("Marked model as missing: " + model.getAlias()); + configurationUpdated = true; + modelsUpdated = true; + } + } else { + if (model.getAlias().endsWith("-missing")) { + model.setAlias(model.getAlias().replaceFirst("-missing$", "")); + System.out.println("Removed -missing suffix from model: " + model.getAlias()); + configurationUpdated = true; + modelsUpdated = true; + } + } + } + } + +} diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/commands/package-info.java b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/package-info.java new file mode 100644 index 0000000..871a60b --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/package-info.java @@ -0,0 +1,13 @@ +/** + *

This package implements all subcommands available in the Älyverkko CLI application. Each command class provides a + * specific functionality through the Command interface. + *

Available commands include: + *

    + *
  • Wizard-style configuration builder
  • + *
  • Model listing and management
  • + *
  • File joining for multi-file processing
  • + *
  • Mail-based AI task processing
  • + *
+ */ + +package eu.svjatoslav.alyverkko_cli.commands; \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/commands/task_processor/Task.java b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/task_processor/Task.java new file mode 100644 index 0000000..6265dd6 --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/task_processor/Task.java @@ -0,0 +1,144 @@ +package eu.svjatoslav.alyverkko_cli.commands.task_processor; + +import eu.svjatoslav.alyverkko_cli.configuration.SkillConfig; +import eu.svjatoslav.alyverkko_cli.model.Model; + +import static eu.svjatoslav.alyverkko_cli.Main.configuration; + +/** + * Represents the data needed to perform a single task based AI query, + * containing prompts and the specific AI model to use. + */ +public class Task { + + /** + * The system prompt text that sets the context or role for the AI. + * This is often used to establish rules or background instructions + * for how the assistant should behave. + */ + public String systemPrompt; + + /** + * The name of the specific skill or capability that the AI should utilize + * when processing the query. This can help determine the focus or purpose + * of the response generated by the AI. + */ + public String skillName; + + public SkillConfig skill; + + /** + * The user's prompt text (the main request or query). + */ + public String userPrompt; + + /** + * The AI model to be used for processing this query. + */ + public Model model; + + /** + * The start time of the query (milliseconds since epoch). + */ + public long startTimeMillis; + + /** + * The end time of the query (milliseconds since epoch). + */ + public long endTimeMillis; + + /** + * Task priority. Bigger integer has higher priority. + */ + public int priority; + + + /** + * Returns a string containing a summary of the {@link Task} object. + * + * @return a string with the system prompt, user prompt, and model info. + */ + @Override + public String toString() { + return "MailQuery{" + + "systemPrompt='" + systemPrompt + '\'' + + ", userPrompt='" + userPrompt + '\'' + + ", model=" + model + + '}'; + } + + /** + * Calculate effective temperature based on override hierarchy. + */ + public float getEffectiveTemperature() { + // 1. Skill specific temperature has priority + if (skill != null && skill.getTemperature() != null) { + return skill.getTemperature(); + } + + // 2. If not in skill, check if model specifies it + if (model.temperature != null) { + return model.temperature; + } + + // 3. Fall back to global default + return configuration.getDefaultTemperature(); + } + + /** + * Calculates effective top-p value using hierarchy: + * skill-specific → model-specific → global default + * + * @return the applicable top-p value for this query + */ + public float getEffectiveTopP() { + // Skill-specific has highest priority + if (skill != null && skill.getTopP() != null) { + return skill.getTopP(); + } + + // Model-specific next + if (model.topP != null) { + return model.topP; + } + + // Global default as fallback + return configuration.getDefaultTopP(); + } + + /** + * Calculates effective repeat penalty using hierarchy: + * skill-specific → model-specific → global default + * + * @return the applicable repeat penalty for this query + */ + public float getEffectiveRepeatPenalty() { + // Skill-specific has highest priority + if (skill != null && skill.getRepeatPenalty() != null) { + return skill.getRepeatPenalty(); + } + + // Model-specific next + if (model.repeatPenalty != null) { + return model.repeatPenalty; + } + + // Global default as fallback + return configuration.getDefaultRepeatPenalty(); + } + + + public float getEffectiveTopK() { + if (skill != null && skill.getTopK() != null) return skill.getTopK(); + else if (model.topK != null) return model.topK.floatValue(); + else return configuration.getDefaultTopK().floatValue(); + } + + public float getEffectiveMinP() { + if (skill != null && skill.getMinP() != null) return skill.getMinP(); + else if (model.minP != null) return model.minP.floatValue(); + else return configuration.getDefaultMinP().floatValue(); + } + + +} diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/commands/task_processor/TaskProcess.java b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/task_processor/TaskProcess.java new file mode 100644 index 0000000..92eb873 --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/task_processor/TaskProcess.java @@ -0,0 +1,298 @@ +package eu.svjatoslav.alyverkko_cli.commands.task_processor; + +import eu.svjatoslav.alyverkko_cli.Utils; + +import java.io.*; +import java.nio.file.Files; + +import static eu.svjatoslav.alyverkko_cli.Main.configuration; +import static java.lang.String.join; + +/** + * + * TODO: what if directory disappeared that contained original input file ? Response cannot be written back anymore. + * + *

Executes AI inference tasks through llama.cpp CLI. This class handles the complete workflow + * from prompt construction to response formatting, including temporary file management and process execution. + *

Key processing steps: + *

    + *
  1. Build standardized input prompt
  2. + *
  3. Create a temporary input file
  4. + *
  5. Execute llama.cpp with appropriate parameters
  6. + *
  7. Capture and filter output
  8. + *
  9. Perform cleanup operations
  10. + *
+ * + *

Temperature settings, context size, and thread counts are all derived from the current configuration. + * The response is formatted to match org-mode conventions while preserving original conversation structure. + */ +public class TaskProcess { + + /** + * Marker used by llama.cpp to print metadata. We monitor and display these lines. + */ + private static final String LLAMA_CPP_META_INFO_MARKER = "llm_load_print_meta: "; + + /** + * The mail query defining system prompt, user prompt, and which model to use. + */ + private final Task task; + + /** + * Temporary file used as input to the llama.cpp CLI. + */ + private File inputFile; + + /** + * Creates a new AI task with a given mail query. + * + * @param task the mail query containing model and prompts. + */ + public TaskProcess(Task task) { + this.task = task; + } + + /** + * Builds the prompt text that is fed to llama.cpp, including the system prompt, + * the user prompt, and an "ASSISTANT:" marker signifying where the AI response begins. + * + * @return a string containing the fully prepared query prompt. + */ + private String buildAiQuery() { + return task.systemPrompt.replace("", task.userPrompt); + } + + /** + * Runs the AI query by constructing the prompt, writing it to a temp file, + * invoking llama.cpp, collecting output, and performing any final cleanup. + * + * @return the AI's response in a format suitable for appending back into + * the conversation file. + * @throws InterruptedException if the process is interrupted. + * @throws IOException if reading/writing the file fails or the process fails to start. + */ + public String runAiQuery() throws InterruptedException, IOException { + try { + // Record the start time of the query + task.startTimeMillis = System.currentTimeMillis(); + + // Build input prompt + initializeInputFile(buildAiQuery()); + + // Prepare a process builder + ProcessBuilder processBuilder = new ProcessBuilder(); + processBuilder.command(getCliCommand().split("\\s+")); // Splitting the command string into tokens + + // Start process + Process process = processBuilder.start(); + + // Handle process's error stream + handleErrorThread(process); + + // Handle the process's output stream + StringBuilder result = new StringBuilder(); + Thread outputThread = handleResultThread(process, result); + + // Wait for the process to finish + process.waitFor(); + + // Wait for the output thread to finish reading + outputThread.join(); + + // Record the end time of the query + task.endTimeMillis = System.currentTimeMillis(); + + // Clean up the AI response: remove partial prompt text, end-of-text marker, etc. + return cleanupAiResponse(result.toString()); + } finally { + deleteTemporaryFile(); + } + } + + /** + * Creates a temporary file for the AI input and writes the prompt to it. + * + * @param aiQuery the final prompt string for the AI to process. + * @throws IOException if file creation or writing fails. + */ + private void initializeInputFile(String aiQuery) throws IOException { + inputFile = createTemporaryFile(); + Files.write(inputFile.toPath(), aiQuery.getBytes()); + } + + /** + * Creates a temporary file that will be used for the AI prompt input. + * + * @return a new {@link File} referencing the created temporary file. + * @throws IOException if the file could not be created. + */ + private File createTemporaryFile() throws IOException { + File file = Files.createTempFile("ai-inference", ".tmp").toFile(); + file.deleteOnExit(); + return file; + } + + /** + * Cleans up the AI response by removing the partial text before the + * AI response marker and after the end-of-text marker, if specified. + * + * @param result the raw output from llama.cpp. + * @return the cleaned AI response. + */ + private String cleanupAiResponse(String result) { + + // remove text after the end of text marker if it exists + if (task.model.endOfTextMarker != null) { + int endOfTextMarkerIndex = result.indexOf(task.model.endOfTextMarker); + if (endOfTextMarkerIndex != -1) { + result = result.substring(0, endOfTextMarkerIndex); + } + } + + return result; + } + + /** + * Returns the full command string used to run the AI inference via llama.cpp. + * + * @return a string representing the command and all arguments. + */ + private String getCliCommand() { + int niceValue = 10; // niceness level for background tasks + String executablePath = configuration.getLlamaCliPath().getAbsolutePath(); + + return join(" ", + "nice", "-n", Integer.toString(niceValue), + executablePath, + "--model " + task.model.filesystemPath, + "--threads " + configuration.getThreadCount(), + "--threads-batch " + configuration.getBatchThreadCount(), + + // Restricts token selection to the smallest possible set of tokens whose cumulative probability + // exceeds the specified threshold P. + "--top-p " + task.getEffectiveTopP(), + + "--repeat-penalty " + task.getEffectiveRepeatPenalty(), + + // Restricts token selection to the K tokens with the highest probabilities. + "--top-k " + task.getEffectiveTopK(), + + // Filters the vocabulary to include only tokens whose + // probability is at least a certain fraction (Min P) of the + // probability of the most likely token. + "--min-p " + task.getEffectiveMinP(), + + // Ensure that model sees the <|im_start|>system … / <|im_start|>user … markup it was trained on + // "--chat-format qwen3", + + // Last n tokens to consider for penalizing repetition + "--repeat-last-n 512", + + // Controls the strength of the penalty for a detected repetition sequence. + //"--dry-multiplier 0.1", + + // In a code we want the model to reuse the same variable names, keywords, and syntax consistently. + // A presence penalty, even a small 0.1, could cause the model to needlessly rename variables. + //"--presence-penalty 1", + + "--mirostat 0", // Disable mirostat + + "--no-display-prompt", + "--no-warmup", + "--flash-attn on", + "--temp " + task.getEffectiveTemperature(), + "--ctx-size " + task.model.contextSizeTokens, + "--batch-size 512", + "--single-turn", // run conversation for a single turn only, then exit when done + "-n -1", + "--file " + inputFile + + // "--cache-type-k q8_0", + // might save RAM, need to test if precision loss is acceptable + + // might save RAM, need to test if precision loss is acceptable + // "--cache-type-v q8_0", + + ); + + + } + + + + + /** + * Spawns a new Thread to handle the error stream from llama.cpp, + * printing lines that contain metadata or errors to the console. + * + * @param process the process whose error stream is consumed. + */ + private static void handleErrorThread(Process process) { + Thread errorThread = new Thread(() -> { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { + String line; + while ((line = reader.readLine()) != null) { + handleErrorStreamLine(line); + } + } catch (IOException e) { + System.err.println("Error reading error stream: " + e.getMessage()); + } + }); + errorThread.start(); + } + + /** + * Decides what to do with each line from the error stream: + * if it matches the llama.cpp meta-info marker, print it normally; + * otherwise print as an error. + * + * @param line a line from the llama.cpp error stream. + */ + private static void handleErrorStreamLine(String line) { + if (line.startsWith(LLAMA_CPP_META_INFO_MARKER)) { + // Print the meta-info to the console in normal color + System.out.println(line.substring(LLAMA_CPP_META_INFO_MARKER.length())); + } else { + // Print actual error lines in red + Utils.printRedMessageToConsole(line); + } + } + + /** + * Consumes the standard output (inference result) from the + * llama.cpp process, storing it into a result buffer for further + * cleanup, while simultaneously printing it to the console. + * + * @param process the AI inference process. + * @param result a string builder to accumulate the final result. + * @return the thread that is reading the output stream. + */ + private static Thread handleResultThread(Process process, StringBuilder result) { + Thread outputThread = new Thread(() -> { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String aiResultLine; + while ((aiResultLine = reader.readLine()) != null) { + System.out.print("AI: " + aiResultLine + "\n"); // Show each line in real-time + result.append(aiResultLine).append("\n"); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + outputThread.start(); + return outputThread; + } + + /** + * Deletes the temporary input file once processing is complete. + */ + private void deleteTemporaryFile() { + if (inputFile != null && inputFile.exists()) { + try { + Files.delete(inputFile.toPath()); + } catch (IOException e) { + System.err.println("Failed to delete temporary file: " + e.getMessage()); + } + } + } +} diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/commands/task_processor/TaskProcessorCommand.java b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/task_processor/TaskProcessorCommand.java new file mode 100644 index 0000000..acfbbf5 --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/task_processor/TaskProcessorCommand.java @@ -0,0 +1,545 @@ +package eu.svjatoslav.alyverkko_cli.commands.task_processor; + +import eu.svjatoslav.alyverkko_cli.*; +import eu.svjatoslav.alyverkko_cli.configuration.ConfigurationHelper; +import eu.svjatoslav.alyverkko_cli.configuration.SkillConfig; +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.FileOption; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.*; +import java.util.*; + +import static eu.svjatoslav.alyverkko_cli.Main.configuration; +import static eu.svjatoslav.commons.file.IOHelper.getFileContentsAsString; +import static eu.svjatoslav.commons.file.IOHelper.saveToFile; +import static java.nio.file.StandardWatchEventKinds.*; + + +/** + * TODO: What happens when directory gets renamed ? Will event listener reindex all files inside it for processing ? + * + * The TaskProcessorCommand continuously monitors a specified tasks + * directory for new or modified text files, checks if they have a + * "TOCOMPUTE:" marker, and if so, adds them to a priority queue to be + * processed in priority order. Once processed, results are appended to + * the same file. + *

+ * Usage: + *

+ *   alyverkko-cli mail
+ * 
+ */ +public class TaskProcessorCommand implements Command { + + /** + * A command-line parser to handle "mail" command arguments. + */ + final Parser parser = new Parser(); + + /** + * Optional CLI argument for specifying a configuration file path. + */ + public FileOption configFileOption = parser.add(new FileOption("Configuration file path")) + .addAliases("--config", "-c") + .mustExist(); + + /** + * The library of available models, constructed from configuration. + */ + ModelLibrary modelLibrary; + + /** + * The WatchService instance for monitoring file system changes in + * the mail directory. + */ + private WatchService directoryWatcher; + + /** + * The directory that we continuously watch for new tasks. + */ + File taskDirectory; + + /** + * Priority queue of tasks to process, sorted by priority and a + * random tiebreaker. + */ + private final PriorityQueue taskQueue; + + public TaskProcessorCommand() { + Comparator comparator = (a, b) -> { + int priorityCompare = Integer.compare(b.priority, a.priority); + if (priorityCompare != 0) { + return priorityCompare; + } + return a.tiebreaker.compareTo(b.tiebreaker); + }; + this.taskQueue = new PriorityQueue<>(comparator); + } + + /** + * @return the name of this command, i.e., "mail". + */ + @Override + public String getCommandName() { + return "process"; + } + + /** + * Executes the "mail" command, loading configuration, starting a + * WatchService on the mail directory, adding existing files to the + * task queue, and processing tasks in priority order. + * + * @param cliArguments the command-line arguments following the "mail" subcommand. + * @throws IOException if reading/writing tasks fails. + * @throws InterruptedException if the WatchService is interrupted. + */ + @Override + public void executeCommand(String[] cliArguments) throws IOException, InterruptedException { + if (!parser.parse(cliArguments)) { + System.out.println("Failed to parse commandline arguments"); + parser.showHelp(); + return; + } + + configuration = ConfigurationHelper.loadConfiguration(ConfigurationHelper.getConfigurationFile(configFileOption)); + if (configuration == null) { + System.out.println("Failed to load configuration file"); + return; + } + + modelLibrary = new ModelLibrary(configuration.getModelsDirectory(), configuration.getModels()); + taskDirectory = configuration.getTaskDirectory(); + + // Set up a directory watch service + initializeFileWatcher(); + + // Add all existing mail files to the queue + initialTaskScanAndReply(); + + System.out.println("Mail correspondent running. Press CTRL+c to terminate."); + + // Main loop: process tasks from the queue in priority order + while (true) { + // Process the highest priority task if available + if (!taskQueue.isEmpty()) processTask(taskQueue.poll()); + + // Check for filesystem events + WatchKey key = directoryWatcher.poll(); + + // Sleep briefly to allow the file to be fully written + Thread.sleep(1000); + + if (key != null) { + System.out.println("Detected filesystem events in mail directory. Processing ... "); + processDetectedFilesystemEvents(key); + + if (!key.reset()) { + break; + } + } + + } + + directoryWatcher.close(); + } + + private void initialTaskScanAndReply() throws IOException { + Files.walk(taskDirectory.toPath()) + .filter(path -> Files.isRegularFile(path)) + .forEach(path -> { + try { + considerFileForQueuing(path); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } + + /** + * Checks if a file needs to be processed by verifying that it: + * 1) is not hidden, + * 2) is a regular file, + * 3) starts with "TOCOMPUTE:" in the first line. + * + * @param file the file to inspect. + * @return true if the file meets the criteria for AI processing. + * @throws IOException if reading the file fails. + */ + private boolean isMailProcessingNeeded(File file) throws IOException { + // ignore hidden files + if (file.getName().startsWith(".")) { + return false; + } + + // Ensure the file exists + if (!file.exists()) { + return false; + } + + // Check if it's a regular file + if (!file.isFile()) { + return false; + } + + // Ensure the first line says "TOCOMPUTE:" + return fileHasToComputeMarker(file); + } + + /** + * Inspects the first line of the file to see if it starts with "TOCOMPUTE:". + * + * @param file the file to read. + * @return true if the file's first line starts with "TOCOMPUTE:". + * @throws IOException if file reading fails. + */ + private static boolean fileHasToComputeMarker(File file) throws IOException { + String firstLine = getFirstLine(file); + return firstLine != null && firstLine.startsWith("TOCOMPUTE:"); + } + + private static String getFirstLine(File file) throws IOException { + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + return reader.readLine(); + } + } + + private static void saveAiResponseToFile(File file, Task task, String aiResponse) throws IOException { + StringBuilder resultFileContent = new StringBuilder(); + + // The First line should be "DONE:" with settings used to process this file + resultFileContent.append(getDoneLine(task)); + + resultFileContent.append("* USER:\n"); + + // Append the original user prompt (after the first line) + resultFileContent.append(task.userPrompt).append("\n"); + + // Append the AI response block + resultFileContent + .append("* ASSISTANT:\n") + .append(aiResponse) + .append("\n"); + + File newFile = new File(file.getParentFile(), "DONE: " + file.getName()); + saveToFile(newFile, resultFileContent.toString()); + + if (!file.delete()) { + System.err.println("Failed to delete original file: " + file.getAbsolutePath()); + } + } + + /** + * Processes a task by reading the file, building the MailQuery, + * running the AI query, and saving the response. + * + * @param entry the task entry containing the file path and priority. + */ + private void processTask(TaskQueueEntry entry) throws IOException { + Path filePath = entry.getFilePath(); + File file = filePath.toFile(); + + if (!isMailProcessingNeeded(file)) { + System.out.println("Ignoring file: " + filePath.getFileName() + " (does not need processing now)"); + return; + } + + try { + Task task = buildMailQueryFromTaskFile(file); + TaskProcess aiTask = new TaskProcess(task); + String aiGeneratedResponse = aiTask.runAiQuery(); + + saveAiResponseToFile(file, task, aiGeneratedResponse); + } catch (IOException | InterruptedException | RuntimeException e) { + e.printStackTrace(); + } + } + + + /** + * Builds a string for the first line of the output file indicating + * that processing is done. + * + * @param task the query that was processed. + * @return a string for the first line. + */ + private static String getDoneLine(Task task) { + return "DONE: skill=" + task.skillName + + " model=" + task.model.alias + + " duration=" + getDuration(task.startTimeMillis, task.endTimeMillis) + "\n"; + } + + + private static String getDuration(long startTimeMillis, long endTimeMillis) { + + long durationSeconds = (endTimeMillis - startTimeMillis) / 1000; + + if (durationSeconds < 180) return durationSeconds + "s"; + + long durationMinutes = durationSeconds / 60; + if (durationMinutes < 180) return durationMinutes + "m"; + + long durationHours = durationMinutes / 60; + return durationHours + "h"; + } + + /** + * Builds a MailQuery object from the contents of a file. + * + * @param file the file to read. + * @return the constructed MailQuery. + * @throws IOException if reading the file fails. + */ + private Task buildMailQueryFromTaskFile(File file) throws IOException { + Task result = new Task(); + + String inputFileContent = getFileContentsAsString(file); + int firstNewLineIndex = inputFileContent.indexOf('\n'); + if (firstNewLineIndex == -1) { + throw new IllegalArgumentException("Input file is only one line long."); + } + + String firstLine = inputFileContent.substring(0, firstNewLineIndex); + Map fileProcessingSettings = parseSettings(firstLine); + + // The rest of the file is the user prompt + result.userPrompt = inputFileContent.substring(firstNewLineIndex + 1); + + // Set system prompt + result.skillName = fileProcessingSettings.getOrDefault("skill", "default"); + SkillConfig skill = configuration.getSkillByName(result.skillName); + result.systemPrompt = skill.getPrompt(); + result.skill = skill; + + // Set AI model + String modelAlias = fileProcessingSettings.getOrDefault("model", "default"); + Optional modelOptional = modelLibrary.findModelByAlias(modelAlias); + if (!modelOptional.isPresent()) { + throw new IllegalArgumentException("Model with alias '" + modelAlias + "' not found."); + } + result.model = modelOptional.get(); + + // Set priority + String priorityStr = fileProcessingSettings.get("priority"); + result.priority = 0; + if (priorityStr != null) { + try { + result.priority = Integer.parseInt(priorityStr); + } catch (NumberFormatException e) { + System.err.println("Invalid priority in file: " + priorityStr); + } + } + + return result; + } + + + /** + * Parses the "TOCOMPUTE:" line, which should look like: + *
TOCOMPUTE: key1=value1 key2=value2 ...
+ * + * @param toComputeLine the line beginning with "TOCOMPUTE:". + * @return a map of settings derived from that line. + */ + private Map parseSettings(String toComputeLine) { + if (!toComputeLine.startsWith("TOCOMPUTE:")) { + throw new IllegalArgumentException("Invalid TOCOMPUTE line: " + toComputeLine); + } + + // If there's nothing beyond "TOCOMPUTE:", just return an empty map + if (toComputeLine.length() <= "TOCOMPUTE: ".length()) { + return new HashMap<>(); + } + + // Example format: "TOCOMPUTE: prompt=writer model=mistral" + String[] parts = toComputeLine.substring("TOCOMPUTE: ".length()).split("\\s+"); + Map settings = new HashMap<>(); + + for (String part : parts) { + String[] keyValue = part.split("="); + if (keyValue.length == 2) { + settings.put(keyValue[0], keyValue[1]); + } + } + return settings; + } + + /** + * Handles the filesystem events from the WatchService (e.g., file creation, modification, or deletion), + * and updates the task queue accordingly. + * + * @param key the watch key containing the events. + * @throws IOException if file processing fails. + */ + private void processDetectedFilesystemEvents(WatchKey key) throws IOException { + Path dir = (Path) key.watchable(); + for (WatchEvent event : key.pollEvents()) { + WatchEvent.Kind kind = event.kind(); + if (kind != ENTRY_CREATE && kind != ENTRY_MODIFY && kind != ENTRY_DELETE) { + continue; + } + @SuppressWarnings("unchecked") + WatchEvent ev = (WatchEvent) event; + Path filename = ev.context(); + Path fullPath = dir.resolve(filename); + System.out.printf("Event: %s – %s%n", kind.name(), fullPath); + if (kind == ENTRY_DELETE) { + // Remove any existing tasks for this file + removeTasksForFile(fullPath); + continue; + } + // Handle directory creation + if (Files.isDirectory(fullPath)) { + if (kind == ENTRY_CREATE) { + // Register the new directory and its subdirectories for monitoring + registerAllSubdirectories(fullPath); + // Scan the new directory for existing files to process + Files.walk(fullPath) + .filter(path -> { + try { + return Files.isRegularFile(path) && !Files.isHidden(path); + } catch (IOException e) { + System.err.println("Failed to check if file is hidden: " + path + " - " + e.getMessage()); + return false; // Skip files that cause errors + } + }) + .forEach(path -> { + try { + considerFileForQueuing(path); + } catch (IOException e) { + System.err.println("Failed to process file in new directory: " + path + " - " + e.getMessage()); + } + }); + } + continue; + } + // Handle file events + if (kind == ENTRY_MODIFY) { + // Remove existing tasks for this file before adding new ones + removeTasksForFile(fullPath); + } + // Check if it's a regular, non-hidden file and needs processing + try { + if (Files.isRegularFile(fullPath) && !Files.isHidden(fullPath)) { + considerFileForQueuing(fullPath); + } + } catch (IOException e) { + System.err.println("Failed to check file: " + fullPath + " - " + e.getMessage()); + } + } + } + + private void initializeFileWatcher() throws IOException { + this.directoryWatcher = FileSystems.getDefault().newWatchService(); + registerAllSubdirectories(taskDirectory.toPath()); + } + + private void registerAllSubdirectories(Path path) { + try { + System.out.println("Registering directory for watch service: " + path); + path.register(directoryWatcher, ENTRY_CREATE, ENTRY_MODIFY); + if (Files.isDirectory(path)) { + try (DirectoryStream stream = Files.newDirectoryStream(path)) { + for (Path entry : stream) { + if (Files.isDirectory(entry)) { + registerAllSubdirectories(entry); + } + } + } + } + } catch (IOException e) { + System.err.println("Failed to register directory: " + path + " - " + e.getMessage()); + } + } + + /** + * Adds a file to the task queue if it needs processing. + * + * @param filePath the path to the file to check. + * @throws IOException if reading the first line fails. + */ + private void considerFileForQueuing(Path filePath) throws IOException { + File file = filePath.toFile(); + if (!isMailProcessingNeeded(file)) return; + + String firstLine = getFirstLine(file); + Map settings = parseSettings(firstLine); + int priority = 0; + String priorityStr = settings.get("priority"); + if (priorityStr != null) { + try { + priority = Integer.parseInt(priorityStr); + } catch (NumberFormatException e) { + System.err.println("Invalid priority in file " + filePath.getFileName() + ": " + priorityStr); + } + } + taskQueue.offer(new TaskQueueEntry(filePath, priority)); + } + + /** + * A static nested class representing a task in the queue, with a + * priority and a tiebreaker for sorting. + */ + private static class TaskQueueEntry implements Comparable { + private final Path filePath; + private final int priority; + private final String tiebreaker; + + public TaskQueueEntry(Path filePath, int priority) { + this.filePath = filePath; + this.priority = priority; + this.tiebreaker = UUID.randomUUID().toString(); + } + + public Path getFilePath() { + return filePath; + } + + public int getPriority() { + return priority; + } + + public String getTiebreaker() { + return tiebreaker; + } + + @Override + public int compareTo(TaskQueueEntry other) { + int priorityCompare = Integer.compare(this.priority, other.priority); + if (priorityCompare != 0) { + return -priorityCompare; // higher priority first + } + return this.tiebreaker.compareTo(other.tiebreaker); + } + } + + /** + * Removes all tasks from the queue that match the given file path. + * This is done by draining the queue into a list, filtering out the matching entries, + * and re-adding the remaining ones back into the queue. + * + * @param filePath the file path to match and remove from the queue. + */ + private void removeTasksForFile(Path filePath) { + List remaining = new ArrayList<>(); + + // Drain all elements into a list + while (!taskQueue.isEmpty()) { + TaskQueueEntry entry = taskQueue.poll(); + if (!entry.getFilePath().equals(filePath)) { + remaining.add(entry); + } + } + + // Re-add all remaining entries back to the queue + for (TaskQueueEntry entry : remaining) { + taskQueue.offer(entry); + } + } + +} diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/commands/task_processor/package-info.java b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/task_processor/package-info.java new file mode 100644 index 0000000..51e74a4 --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/task_processor/package-info.java @@ -0,0 +1,12 @@ +/** + *

This subpackage implements the mail-based AI task processing functionality. It watches mail directories for + * new/modified files with specific markers and processes them using AI models. + *

Key components: + *

    + *
  • File monitoring with WatchService
  • + *
  • Prompt parsing and execution logic
  • + *
  • Query object for storing processing parameters
  • + *
+ */ + +package eu.svjatoslav.alyverkko_cli.commands.task_processor; \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/Configuration.java b/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/Configuration.java new file mode 100644 index 0000000..6872648 --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/Configuration.java @@ -0,0 +1,118 @@ +package eu.svjatoslav.alyverkko_cli.configuration; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.*; +import java.util.List; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; + + +/** + *

Central configuration class storing all application parameters. + * This class is serialized to YAML format for user editing and persistence. + *

Configuration parameters include: + *

    + *
  • Model and prompt directories
  • + *
  • Performance tuning parameters
  • + *
  • Model-specific configurations
  • + *
+ *

All paths are resolved relative to the user's home directory by default, but can be customized. The class provides + * direct access to prompt content for AI query construction. + */ +@Data +public class Configuration { + + /** + * Directory where AI tasks (mail) are placed and discovered. + */ + @JsonProperty("task_directory") + private File taskDirectory; + + /** + * Directory that contains AI model files in GGUF format. + */ + @JsonProperty("models_directory") + private File modelsDirectory; + + /** + * The default "temperature" used by the AI for creative/deterministic + * tradeoff. Ranges roughly between 0 and 3. + */ + @JsonProperty("default_temperature") + private Float defaultTemperature; + + /** + * Default top-p value used when not specified elsewhere. + * Controls diversity of sampling (0.0-1.0, where 1.0 means no restriction). + */ + @JsonProperty("default_top_p") + private Float defaultTopP; + + /** + * Default global Top-K value used when not specified elsewhere. + */ + @JsonProperty("default_top_k") + private Float defaultTopK = 30f; + + /** + * Global minimum-p threshold default (applies if none set). + */ + @JsonProperty("default_min_p") + private Float defaultMinP = 0f; + + /** + * Default repeat penalty used when not specified elsewhere. + * Controls repetition reduction (typically 0.8-2.0, where 1.0 means no penalty). + */ + @JsonProperty("default_repeat_penalty") + private Float defaultRepeatPenalty; + + /** + * The filesystem path to the llama-cli executable, which processes + * AI tasks via llama.cpp. + */ + @JsonProperty("llama_cli_path") + private File llamaCliPath; + + /** + * Number of CPU threads used for input prompt processing. + */ + @JsonProperty("batch_thread_count") + private Integer batchThreadCount; + + /** + * Number of CPU threads used for AI inference. + */ + @JsonProperty("thread_count") + private Integer threadCount; + + /** + * Directory containing text prompt files. Each file is a separate + * "prompt" by alias (the filename minus ".txt"). + */ + @JsonProperty("skills_directory") + private File skillsDirectory; + + /** + * The list of models defined in this configuration. + */ + private List models; + + + /** + * Retrieves the contents of a prompt file by alias, e.g. "writer" + * maps to "writer.txt" in the prompt's directory. + * + * @param skillName the name of the prompt file (without ".txt"). + * @return the full-text content of the prompt file. + * @throws IOException if reading the prompt file fails. + */ + public SkillConfig getSkillByName(String skillName) throws IOException { + File promptFile = new File(skillsDirectory, skillName + ".yaml"); + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + return mapper.readValue(promptFile, SkillConfig.class); + } + +} diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/ConfigurationHelper.java b/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/ConfigurationHelper.java new file mode 100644 index 0000000..6c39b8a --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/ConfigurationHelper.java @@ -0,0 +1,58 @@ +package eu.svjatoslav.alyverkko_cli.configuration; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import eu.svjatoslav.commons.cli_helper.parameter_parser.parameter.FileOption; + +import java.io.File; +import java.io.IOException; + +/** + *

Helper class for configuration file operations. Provides methods for loading configurations + * and determining the default configuration file path in the user's home directory. + *

Key functionality includes: + *

    + *
  • Configuration file path resolution
  • + *
  • YAML deserialization
  • + *
  • Error handling for missing configurations
  • + *
+ */ +public class ConfigurationHelper { + + /** + * The default path for the YAML config file, typically under the user's home directory. + */ + public static final String DEFAULT_CONFIG_FILE_PATH = "~/.config/alyverkko-cli/alyverkko-cli.yaml".replaceFirst("^~", System.getProperty("user.home")); + + /** + * Loads the configuration from a given file, or from the default + * path if {@code configFile} is null. + * + * @param configFile the file containing the YAML config; may be null. + * @return the {@link Configuration} object, or null if not found/invalid. + * @throws IOException if file I/O fails during reading. + */ + public static Configuration loadConfiguration(File configFile) throws IOException { + + if (!configFile.exists()) { + System.err.println("Configuration file not found: " + configFile); + return null; + } + + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + return mapper.readValue(configFile, Configuration.class); + } + + /** + * Returns the configuration file from the given option, or the default path if not present. + * @param configFileOption the CLI option for the config file. + * @return the configuration file to load. + */ + public static File getConfigurationFile(FileOption configFileOption) { + if (configFileOption != null) + if (configFileOption.isPresent()) + return configFileOption.getValue(); + + return new File(DEFAULT_CONFIG_FILE_PATH); + } +} diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/ConfigurationModel.java b/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/ConfigurationModel.java new file mode 100644 index 0000000..9588068 --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/ConfigurationModel.java @@ -0,0 +1,62 @@ +package eu.svjatoslav.alyverkko_cli.configuration; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * Represents a single AI model configuration entry, including alias, + * path to the model file, token context size, and an optional + * end-of-text marker. + */ +@Data +public class ConfigurationModel { + + /** + * A short name for the model, e.g., "default" or "mistral". + */ + private String alias; + + /** + * Model-specific temperature value overriding global default. + */ + private Float temperature; + + /** + * Model-specific top-p value overriding global default. + */ + @JsonProperty("top_p") + private Float topP; + + @JsonProperty("min_p") + private Float minP; + + @JsonProperty("top_k") + private Float topK; + + /** + * Model-specific repeat penalty value overriding global default. + */ + @JsonProperty("repeat_penalty") + private Float repeatPenalty; + + + /** + * The path to the model file (GGUF, etc.), relative to + * {@link Configuration#getModelsDirectory()} or fully qualified. + */ + @JsonProperty("filesystem_path") + private String filesystemPath; + + /** + * The maximum context size the model supports, in tokens. + */ + @JsonProperty("context_size_tokens") + private int contextSizeTokens; + + /** + * Optional text marker signifying the end of text for this model. + * If non-null, it will be used to strip trailing tokens from the AI response. + */ + @JsonProperty("end_of_text_marker") + private String endOfTextMarker; +} diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/SkillConfig.java b/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/SkillConfig.java new file mode 100644 index 0000000..2ccd61c --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/SkillConfig.java @@ -0,0 +1,29 @@ +package eu.svjatoslav.alyverkko_cli.configuration; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class SkillConfig { + + private String prompt; + + private Float temperature; + + /** + * Skill-specific top-p value overriding model/global defaults. + */ + private Float topP; + + @JsonProperty("top_k") + private Float topK; + + @JsonProperty("min_p") + private Float minP; + + /** + * Skill-specific repeat penalty value overriding model/global defaults. + */ + private Float repeatPenalty; + +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/package-info.java b/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/package-info.java new file mode 100644 index 0000000..fd21d0b --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/package-info.java @@ -0,0 +1,13 @@ +/** + *

This package handles the configuration system for the Älyverkko CLI application. + * It provides classes for storing and retrieving application-wide settings, including + * model configurations, directory paths, and performance parameters. + *

Configuration is stored in YAML format and includes: + *

    + *
  • Model directory paths
  • + *
  • Mail task directories
  • + *
  • Performance settings (thread counts, temperature)
  • + *
+ */ + +package eu.svjatoslav.alyverkko_cli.configuration; \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/model/Model.java b/src/main/java/eu/svjatoslav/alyverkko_cli/model/Model.java new file mode 100644 index 0000000..935ccd6 --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/model/Model.java @@ -0,0 +1,109 @@ +package eu.svjatoslav.alyverkko_cli.model; + +import java.io.File; + +/** + *

Represents an AI model stored on the filesystem with metadata about its capabilities and identification. + * This class serves as a lightweight container for model information, enabling quick lookup and validation. + *

Models are typically discovered through configuration files and stored in the ModelLibrary for easy access. + *

Key fields include: + *

    + *
  • filesystemPath - Location of the model file
  • + *
  • contextSizeTokens - Maximum token capacity for this model
  • + *
  • alias - User-friendly identifier for the model
  • + *
  • endOfTextMarker - Optional response completion marker
  • + *
+ */ +public class Model { + + /** + * The path to the model file on the filesystem. + */ + public final File filesystemPath; + + /** + * The size of the context (in tokens) that this model is able to handle. + */ + public final int contextSizeTokens; + + /** + * A user-friendly alias for the model, e.g. "default" or "mistral". + */ + public final String alias; + + /** + * An optional marker indicating end of the AI-generated text (e.g., "###"). + * If non-null, it can be used to detect where the model has finished answering. + */ + public final String endOfTextMarker; + + /** + * Model-specific temperature value (null if not configured). + */ + public Float temperature; + + /** + * Model-specific top-p value (null if not configured). + */ + public Float topP; + + /** + * Model-specific Top-K parameter controlling token selection (null if not configured). + */ + public Float topK; + + /** + * Minimum probability threshold for candidate tokens (null if not configured). + */ + public Float minP; + + /** + * Model-specific repeat penalty value (null if not configured). + */ + public Float repeatPenalty; + + /** + * Constructs a {@link Model} instance with all hyperparameters. + * + * @param filesystemPath The path to the model file on the filesystem. + * @param contextSizeTokens Maximum token capacity of this model. + * @param modelAlias User-friendly identifier for the model. + * @param endOfTextMarker Optional response completion marker. + * @param temperature Sampling temperature (higher = more creative) + * @param topP Nucleus sampling threshold (0.0-1.0) + * @param repeatPenalty Penalty for token repetition (>0.0) + * @param topK Token selection cutoff for Top-K sampling (>=1) + * @param minP Minimum relative probability threshold (0.0-1.0) + */ + public Model(File filesystemPath, int contextSizeTokens, + String modelAlias, String endOfTextMarker, + Float temperature, Float topP, Float repeatPenalty, + Float topK, Float minP) { + this.filesystemPath = filesystemPath; + this.contextSizeTokens = contextSizeTokens; + this.alias = modelAlias; + this.endOfTextMarker = endOfTextMarker; + this.temperature = temperature; + this.topP = topP; + this.repeatPenalty = repeatPenalty; + this.topK = topK; + this.minP = minP; + } + + /** + *

Prints the model's metadata to standard output in a consistent format. This includes the model's alias, + * filesystem path, and context token capacity. The output format is designed to be both human-readable and + * machine-parsable when needed. + *

Typical output: + *

+     * Model: default
+     *   Path: /path/to/model.gguf
+     *   Context size: 32768
+     * 
+ */ + public void printModelDetails() { + System.out.println("Model: " + alias); + System.out.println(" Path: " + filesystemPath); + System.out.println(" Context size: " + contextSizeTokens); + } +} diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/model/ModelLibrary.java b/src/main/java/eu/svjatoslav/alyverkko_cli/model/ModelLibrary.java new file mode 100644 index 0000000..4c94c92 --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/model/ModelLibrary.java @@ -0,0 +1,131 @@ +package eu.svjatoslav.alyverkko_cli.model; + +import eu.svjatoslav.alyverkko_cli.Utils; +import eu.svjatoslav.alyverkko_cli.configuration.ConfigurationModel; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * A container (library) for multiple AI models, providing + * functionality for adding and retrieving models by alias. + */ +public class ModelLibrary { + + /** + * The list of all successfully loaded models in this library. + */ + private final List models; + + /** + * The default model for this library (e.g., the first successfully + * loaded model in the list). + */ + private static Model defaultModel; + + /** + * Base directory containing the model files. + */ + private final File modelsBaseDirectory; + + /** + * Constructs a library of AI models from the provided list of + * {@link ConfigurationModel}s, ignoring those whose paths do not exist. + * + * @param modelsBaseDirectory the root directory where model files are stored. + * @param configModels a list of model configurations. + */ + public ModelLibrary(File modelsBaseDirectory, List configModels) { + this.modelsBaseDirectory = modelsBaseDirectory; + this.models = new ArrayList<>(); + + for (ConfigurationModel configModel : configModels) { + addModelFromConfig(configModel); + } + + if (models.isEmpty()) { + throw new RuntimeException("No models are defined!"); + } + + defaultModel = models.get(0); + } + + /** + * Attempts to construct a {@link Model} from configuration, verifying file existence. + */ + private void addModelFromConfig(ConfigurationModel configModel) { + File modelFile = new File(modelsBaseDirectory, configModel.getFilesystemPath()); + if (!modelFile.exists()) { + Utils.printRedMessageToConsole("WARN: Model file not found: " + modelFile.getAbsolutePath() + " . Skipping model."); + return; + } + + addModel(new Model( + modelFile, + configModel.getContextSizeTokens(), + configModel.getAlias(), + configModel.getEndOfTextMarker(), + + configModel.getTemperature(), + configModel.getTopP(), + configModel.getRepeatPenalty(), + + configModel.getTopK(), + configModel.getMinP() + )); + + } + + /** + * Adds a model to the library if no model with the same alias + * already exists. + * + * @param model the model to add. + * @throws RuntimeException if a model with the same alias already exists. + */ + public void addModel(Model model) { + if (findModelByAlias(model.alias).isPresent()) { + throw new RuntimeException("Model with alias \"" + model.alias + "\" already exists!"); + } + models.add(model); + } + + /** + * @return the list of loaded models in this library. + */ + public List getModels() { + return models; + } + + /** + * Finds a model by its alias in this library. + * + * @param alias the model alias to look for. + * @return an {@link Optional} describing the found model, or empty if none match. + */ + public Optional findModelByAlias(String alias) { + return models.stream() + .filter(model -> model.alias.equals(alias)) + .findFirst(); + } + + /** + * @return the default model (first loaded model). + */ + public Model getDefaultModel() { + return defaultModel; + } + + /** + * Prints the details of each model in the library to standard output. + */ + public void printModels() { + System.out.println("Available models:\n"); + for (Model model : models) { + model.printModelDetails(); + System.out.println(); + } + } +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/model/package-info.java b/src/main/java/eu/svjatoslav/alyverkko_cli/model/package-info.java new file mode 100644 index 0000000..5d8b074 --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/model/package-info.java @@ -0,0 +1,12 @@ +/** + *

This package defines the model management system for the Älyverkko CLI. + * It includes classes for representing AI models and maintaining a library of available models. + *

Key features: + *

    + *
  • Model metadata storage and validation
  • + *
  • Default model selection and management
  • + *
  • File system integration for model discovery
  • + *
+ */ + +package eu.svjatoslav.alyverkko_cli.model; \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/package-info.java b/src/main/java/eu/svjatoslav/alyverkko_cli/package-info.java new file mode 100644 index 0000000..a3057d0 --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/package-info.java @@ -0,0 +1,14 @@ +/** + *

This package contains the core components of the Älyverkko CLI application, including the main entry point, + * command interfaces, and utility classes. It serves as the central hub for orchestrating subcommands and core + * application behavior. + *

Key responsibilities include: + *

    + *
  • Command registration and execution
  • + *
  • Configuration loading and management
  • + *
  • Basic utility functions for colored console output
  • + *
+ * + */ + +package eu.svjatoslav.alyverkko_cli; \ No newline at end of file diff --git a/tools/implement idea b/tools/implement idea new file mode 100755 index 0000000..6472735 --- /dev/null +++ b/tools/implement idea @@ -0,0 +1,12 @@ +#!/bin/bash +cd "${0%/*}"; if [ "$1" != "T" ]; then gnome-terminal -e "'$0' T"; exit; fi; +cd .. + +read -p "Enter the topic name: " TOPIC_NAME + +alyverkko-cli joinfiles -t "$TOPIC_NAME" --src-dir . --pattern "*.org" +alyverkko-cli joinfiles -t "$TOPIC_NAME" --src-dir . --pattern "*.java" +alyverkko-cli joinfiles -t "$TOPIC_NAME" --src-dir . --pattern "implement*" +alyverkko-cli joinfiles -t "$TOPIC_NAME" --src-dir . --pattern "install" +alyverkko-cli joinfiles -t "$TOPIC_NAME" --src-dir . --pattern "uninstall" +alyverkko-cli joinfiles -t "$TOPIC_NAME" --edit diff --git a/tools/open with IntelliJ IDEA b/tools/open with IntelliJ IDEA new file mode 100755 index 0000000..304bf94 --- /dev/null +++ b/tools/open with IntelliJ IDEA @@ -0,0 +1,54 @@ +#!/bin/bash + +# This script launches IntelliJ IDEA with the current project +# directory. The script is designed to be run by double-clicking it in +# the GNOME Nautilus file manager. + +# First, we change the current working directory to the directory of +# the script. + +# "${0%/*}" gives us the path of the script itself, without the +# script's filename. + +# This command basically tells the system "change the current +# directory to the directory containing this script". + +cd "${0%/*}" + +# Then, we move up one directory level. +# The ".." tells the system to go to the parent directory of the current directory. +# This is done because we assume that the project directory is one level up from the script. +cd .. + +# Now, we use the 'setsid' command to start a new session and run +# IntelliJ IDEA in the background. 'setsid' is a UNIX command that +# runs a program in a new session. + +# The command 'idea .' opens IntelliJ IDEA with the current directory +# as the project directory. The '&' at the end is a UNIX command that +# runs the process in the background. The '> /dev/null' part tells +# the system to redirect all output (both stdout and stderr, denoted +# by '&') that would normally go to the terminal to go to /dev/null +# instead, which is a special file that discards all data written to +# it. + +setsid idea . &>/dev/null & + +# The 'disown' command is a shell built-in that removes a shell job +# from the shell's active list. Therefore, the shell will not send a +# SIGHUP to this particular job when the shell session is terminated. + +# '-h' option specifies that if the shell receives a SIGHUP, it also +# doesn't send a SIGHUP to the job. + +# '$!' is a shell special parameter that expands to the process ID of +# the most recent background job. +disown -h $! + + +sleep 2 + +# Finally, we use the 'exit' command to terminate the shell script. +# This command tells the system to close the terminal window after +# IntelliJ IDEA has been opened. +exit diff --git a/tools/update web site b/tools/update web site new file mode 100755 index 0000000..5d3b485 --- /dev/null +++ b/tools/update web site @@ -0,0 +1,35 @@ +#!/bin/bash +cd "${0%/*}"; if [ "$1" != "T" ]; then gnome-terminal -e "'$0' T"; exit; fi; + +cd .. + +# Build the project jar file and the apidocs. +mvn clean package + +# Export org to html using emacs in batch mode +( + cd doc/ + + rm -f index.html + emacs --batch -l ~/.emacs --visit=index.org --funcall=org-html-export-to-html --kill + + #rm setup.html + #emacs --batch -l ~/.emacs --visit=setup.org --funcall=org-html-export-to-html --kill +) + +# Generate class diagrams. See: https://www3.svjatoslav.eu/projects/javainspect/ +rm -rf doc/graphs/ +mkdir -p doc/graphs/ +javainspect -j target/alyverkko-cli-*-SNAPSHOT.jar -d doc/graphs/ -n "all classes" -t png -ho +meviz index -w doc/graphs/ -t "Älyverkko CLI program classes" + +# Copy the apidocs to the doc folder so that they can be uploaded to the server. +rm -rf doc/apidocs/ +cp -r target/apidocs/ doc/ + +# Upload project homepage to the server. +rsync -avz --delete -e 'ssh -p 10006' doc/ n0@www3.svjatoslav.eu:/mnt/big/projects/alyverkko-cli/ + +echo "" +echo "Press ENTER to close this window." +read diff --git a/uninstall b/uninstall new file mode 100755 index 0000000..b4a4474 --- /dev/null +++ b/uninstall @@ -0,0 +1,13 @@ +#!/bin/bash + +SYSTEMD_SERVICE_FILE="/etc/systemd/system/alyverkko-cli.service" + +sudo systemctl stop alyverkko-cli +sudo systemctl disable alyverkko-cli +sudo rm "$SYSTEMD_SERVICE_FILE" +sudo rm -rf /opt/alyverkko-cli/ + +read -p "Do you want to remove user configuration as well? (y/N) " remove_config +if [[ $remove_config == [Yy] ]]; then + sudo rm -rf "${HOME}/.config/alyverkko-cli" +fi -- 2.20.1