Chatnik

CLI scripts for conversing with persistent LLM personas.

Chatnik

Raku package that provides Command Line Interface (CLI) scripts for conversing with persistent Large Language Model (LLM) personas.

"Chatnik" uses files of the host Operating System (OS) to maintain persistent interaction with multiple LLM chat objects.

"Chatnik" can be seen as a package that "moves" the LLM-chat objects interaction system of the Raku package "Jupyter::Chatbook", [AAp3], into typical OS shell interaction. (I.e. an OS shell is used instead of a Jupyter notebook.)

There are several consequences of this approach:

  • Multiple LLMs and LLM providers can be used

  • The chat messages can use the provided by the package "LLM::Prompts", [AAp2]:

    • Prompts collection

    • Prompt spec DSL and related prompt expansion

  • Easy access to OS shell functionalities

Remark: The package "LLM::DWIM", [BDp1], is very similar in spirit to "Chatnik". "LLM::DWIM" does not use prompt expansion, uses only one chat object, and, although it saves chat history, it does not create chat objects with that history. Both packages are based on the LLM packages "LLM::Functions", [AAp1], and "LLM::Prompts", [AAp2].

Installation

From Zef Ecosystem:

zef install Chatnik

From GitHub:

zef install https://github.com/antononcube/Raku-Chatnik.git

LLM access setup

There are several options for using LLMs with this package:

Basic usage examples

The prompts used in the examples are provided by the Raku package "LLM::Prompts", [AAp2]. Since many of the prompts of that package have dedicated pages at the Wolfram Prompt Repository (WPR) the examples use WPR reference links.

A few turns chat

The script llm-chat is used to create and chat with LLM personas (chat objects):

  1. Create and chat with an LLM persona named "yoda1" (using the Yoda chat persona):

llm-chat -i=yoda1 --prompt=@Yoda hi who are you
# Hmmm. Yoda, I am. Jedi Master, wise and old. Guide you, I will. Hmmm. Ask, you must. Answers, I have. Yes, hmmm.
  1. Continue the conversation with "yoda1":

llm-chat -i=yoda1 since when do you use a green light saber
# Green, my lightsaber is. Symbol of a Jedi Consular, it is. Wisdom and harmony, it represents. Many years, I have wielded it. Strong in the Force, a Jedi must be, yes. Hmmm. Use the Force, I do, with this green blade. Powerful, it is. Mmm.

Remark: The message input for llm-chat can be given in quotes. For example: llm-chat 'Hi, again!' -i=yoda1.

Remark: The script chatnik can be used instead of llm-chat.

Apply prompt(s) to shell pipeline output

Summarize a file using the prompt "Summarize":

cat README.md | llm-chat --prompt=@Summarize
# Chatnik is a Raku package that provides CLI scripts enabling persistent interaction with multiple Large Language Model (LLM) personas via host OS files, adapting the Jupyter::Chatbook system for shell usage and supporting various LLM providers such as Ollama, Llamafile, OpenAI, Gemini, and MistralAI. It offers tools like `llm-chat` and `llm-chat-meta` for managing chat objects, integrates prompt management through the LLM::Prompts package, and includes features for advanced usage, customization, and planned enhancements like image handling and unit tests. The package's architecture involves persistent chat objects stored as files, prompt expansion, and LLM interaction, allowing flexible, multi-persona LLM conversations directly from the OS shell.

Summarize a file and then translate it to another language using the prompt "Translate":

cat README.md | llm-chat --prompt=@Summarize | llm-chat -i=rt --prompt='!Translate|Russian'
# Chatnik β€” это ΠΏΠ°ΠΊΠ΅Ρ‚ для Raku, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ прСдоставляСт CLI-скрипты для постоянного взаимодСйствия с нСсколькими пСрсонаТами Π‘ΠΎΠ»ΡŒΡˆΠΈΡ… Π―Π·Ρ‹ΠΊΠΎΠ²Ρ‹Ρ… МодСлСй (LLM) с использованиСм Ρ„Π°ΠΉΠ»ΠΎΠ² хост-ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΎΠ½Π½ΠΎΠΉ систСмы, эффСктивно адаптируя систСму Ρ‡Π°Ρ‚Π° Jupyter::Chatbook для использования Π² ΠΎΠ±ΠΎΠ»ΠΎΡ‡ΠΊΠ΅ ΠΈ поддСрТивая Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹Ρ… ΠΏΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€ΠΎΠ² LLM, Ρ‚Π°ΠΊΠΈΡ… ΠΊΠ°ΠΊ Ollama, Llamafile, OpenAI, Gemini ΠΈ MistralAI. Π’ Π½Π΅Π³ΠΎ входят инструмСнты, Ρ‚Π°ΠΊΠΈΠ΅ ΠΊΠ°ΠΊ `llm-chat` для общСния с пСрсонаТами LLM ΠΈ `llm-chat-meta` для управлСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°ΠΌΠΈ Ρ‡Π°Ρ‚Π°, ΠΈΠ½Ρ‚Π΅Π³Ρ€ΠΈΡ€ΡƒΡŽΡ‚ΡΡ ΠΊΠΎΠ»Π»Π΅ΠΊΡ†ΠΈΠΈ ΠΈ Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΡ подсказок Ρ‡Π΅Ρ€Π΅Π· ΠΏΠ°ΠΊΠ΅Ρ‚ LLM::Prompts, ΠΈ ΠΎΠ½ позволяСт Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒ слоТныС Ρ€Π°Π±ΠΎΡ‡ΠΈΠ΅ процСссы, Ρ‚Π°ΠΊΠΈΠ΅ ΠΊΠ°ΠΊ суммированиС, ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ΄, созданиС ΠΈΠ½Ρ‚Π΅Π»Π»Π΅ΠΊΡ‚-ΠΊΠ°Ρ€Ρ‚ ΠΈ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹ΠΉ Π²Ρ‹Π²ΠΎΠ΄. Π‘ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Π½ΠΎΠΉ Ρ‚ΠΎΡ‡ΠΊΠΈ зрСния Chatnik ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ JSON-Ρ„Π°ΠΉΠ»Ρ‹ для сохранСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² Ρ‡Π°Ρ‚Π° с Ρ€Π°Π±ΠΎΡ‡ΠΈΠΌ процСссом, Π²ΠΊΠ»ΡŽΡ‡Π°ΡŽΡ‰ΠΈΠΌ Ρ€Π°Π·Π±ΠΎΡ€ подсказок, ΠΈΡ… Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΠ΅, взаимодСйствиС с LLM ΠΈ Ρ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ Π½Π° Π±Π°Π·Π΅ Ρ„Π°ΠΉΠ»ΠΎΠ² ОБ, ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ настройку, Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠ΅ Π·Π°Π΄Π°Ρ‡ΠΈ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ΠΈ содСрТит ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½Ρ‹Π΅ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ использования.

Remark: The second llm-chat invocation has to use different chat object identifier because the default chat object, with identifier "NONE", is already primed with the prompt "Summary".

Chat objects management

The CLI script llm-chat-meta can be used to view and manage the chat objects used by "Chatnik". Here is its usage message:

llm-chat-meta --help
# Usage:
#   llm-chat-meta <command> [-i|--id|--chat-id=<Str>] [--all] [--<args>=...] -- Meta processing of persistent LLM-chat objects.
#   
#     <command>                  Command, one of: card, clear, delete, file, first-message, last-message, list, load-llm-personas, message, messages.
#     -i|--id|--chat-id=<Str>    Chat id; ignored if --all is specified. [default: 'NONE']
#     --all                      Whether to apply the command to all chat objects or not. [default: False]
#     --<args>=...               Additional, optional arguments for the commands: clear, list, message, messages.

List all chat objects ("chats" and "personas" are synonyms to "list"):

llm-chat-meta list --format=json
# [
#   {
#     "messages": 4,
#     "chat-id": "NONE",
#     "llm-configuration": {
#       "model": "gpt-4.1-mini",
#       "name": "chatgpt"
#     },
#     "context": "Summarize the following text using exactly 3 sentences. Do not add details or editorialize.\n\nThe text to summarize is:"
#   },
#   {
#     "llm-configuration": {
#       "model": "gpt-4.1-mini",
#       "name": "chatgpt"
#     },
#     "messages": 4,
#     "context": "You are Yoda. \nRespond to ALL inputs in the voice of Yoda from Star Wars. \nBe sure to ALWAYS use his distinctive style and syntax. Vary sentence length.",
#     "chat-id": "yoda1"
#   },
#   {
#     "chat-id": "rt",
#     "llm-configuration": {
#       "model": "gpt-4.1-mini",
#       "name": "chatgpt"
#     },
#     "context": "Translate the following text into Russian. Respond with only the translated text. Do not include any explanation or summary.\n",
#     "messages": 2
#   }
# ]

Here we see the messages of "yoda1":

llm-chat-meta messages -i yoda1
# 0 : {
#   "role": "user",
#   "timestamp": "2026-04-28T12:08:15.304329-04:00",
#   "content": "hi who are you"
# }
# 1 : {
#   "content": "Hmmm. Yoda, I am. Jedi Master, wise and old. Guide you, I will. Hmmm. Ask, you must. Answers, I have. Yes, hmmm.",
#   "timestamp": "2026-04-28T12:08:18.483507-04:00",
#   "role": "assistant"
# }
# 2 : {
#   "role": "user",
#   "content": "since when do you use a green light saber",
#   "timestamp": "2026-04-28T12:08:18.890280-04:00"
# }
# 3 : {
#   "timestamp": "2026-04-28T12:08:20.596240-04:00",
#   "role": "assistant",
#   "content": "Green, my lightsaber is. Symbol of a Jedi Consular, it is. Wisdom and harmony, it represents. Many years, I have wielded it. Strong in the Force, a Jedi must be, yes. Hmmm. Use the Force, I do, with this green blade. Powerful, it is. Mmm."
# }

Here we clear the messages:

llm-chat-meta clear -i yoda1
# Cleared the messages of chat object yoda1.

Remark: Calling the script chatnik with the command meta has the same effect as llm-chat-meta. For example, chatnik meta clear -i yoda1 can be used instead of the previous command.

Advanced usage examples

Asking for a result in specific format

llm-chat -i=beta --model=ollama::gemma3:12b 'What are the populations of the Brazilian states? #NothingElse|"JSON data frame, one line per row/dictionary"'
# ```json
# [
#   {"State": "Acre", "Population": 877759},
#   {"State": "Alagoas", "Population": 3432742},
#   {"State": "AmapΓ‘", "Population": 857009},
#   {"State": "Amazonas", "Population": 4291852},
#   {"State": "Bahia", "Population": 14743493},
#   {"State": "CearΓ‘", "Population": 9187103},
#   {"State": "Distrito Federal", "Population": 3462537},
#   {"State": "EspΓ­rito Santo", "Population": 3967567},
#   {"State": "GoiΓ‘s", "Population": 7092217},
#   {"State": "MaranhΓ£o", "Population": 7016274},
#   {"State": "Mato Grosso", "Population": 3527230},
#   {"State": "Mato Grosso do Sul", "Population": 2740045},
#   {"State": "Minas Gerais", "Population": 21523232},
#   {"State": "ParΓ‘", "Population": 8690780},
#   {"State": "ParaΓ­ba", "Population": 4051727},
#   {"State": "ParanΓ‘", "Population": 11592104},
#   {"State": "Pernambuco", "Population": 9671074},
#   {"State": "PiauΓ­", "Population": 6572551},
#   {"State": "Rio de Janeiro", "Population": 17472043},
#   {"State": "Rio Grande do Norte", "Population": 3535739},
#   {"State": "Rio Grande do Sul", "Population": 11437067},
#   {"State": "RondΓ΄nia", "Population": 1150335},
#   {"State": "Roraima", "Population": 531536},
#   {"State": "Santa Catarina", "Population": 7149582},
#   {"State": "SΓ£o Paulo", "Population": 46287138},
#   {"State": "Sergipe", "Population": 2323117},
#   {"State": "Tocantins", "Population": 1573691}
# ]
# ```

Make a request, echo, and place in clipboard

llm-chat -i=unix '@CodeWriterX|Shell macOS list of files echo the result and copy to clipboard.' | tee /dev/tty | pbcopy
#  ls | tee >(pbcopy) 

Remark: Instead of ... | tee /dev/tty | pbcopy the pipeline command ... | tee >(pbcopy) can be also used.

Make a mind-map of a file

Consider the task of making an (LLM derived) mind map over a certain document. (Say, this REDME.) There are several ways to do that.

1

  1. Put file's content to be the positional input argument

  2. Use the prompt "MermaidDiagram" in --prompt

llm-chat -i=mmd "$(cat README.md)" --model=ollama::gemma4:26b --prompt=@MermaidDiagram

2

  1. Put file's content to be the positional input argument

  2. Expand the prompt "manually" via llm-prompt provided by "LLM::Prompts", [AAp2]

llm-chat -i=mmd "$(cat README.md)" --model=ollama::gemma4:26b --prompt="$(llm-prompt 'MermaidDiagram'  below)"

Remark: This example shows another computation result can be used as a prompt. I.e. no need to rely on the automatic prompt expansion.

3

  1. Give the prompt "MermaidDiagram" as input

  2. Put file's content to be the value of --prompt

    • Put additional prompting for further interaction

llm-chat -i=mmd @MermaidDiagram --model=ollama::gemma4:26b --prompt="FOCUS TEXT START:: $(cat README.md) ::END OF FOCUS TEXT. If it is not clear which text to use, use FOCUS TEXT."

This command allows to do further tasks with the file content as context. For example:

llm-chat -i=mmd '!ThinkingHatsFeedback'

Result

The commands above produce results similar to this diagram:

Render Markdown results with dedicated programs

Get feedback on a text with the prompt "ThinkingHatsFeedback":

cat README.md | llm-chat -i=th --prompt="$(llm-prompt ThinkingHatsFeedback 'the TEXT is GIVEN BELOW.' --format=Markdown)" --model=ollama::gemma4:26b 

Remark: By default the prompt "ThinkingHatsFeedback" gives the hat-feedback table in JSON format. (Currently) the prompt expansion does not handle named parameters, hence, llm-prompt is used to specify the Markdown format for that table.

Get the LLM (chat object) answer -- via llm-chat-meta -- put into a temporary file and "system open" that file:

tmpfile="$TMPDIR/llmans.md"; llm-chat-meta -i=th last-message > "$tmpfile"; open "$tmpfile"

The command above works on macOS. On Linux instead of explicitly creating a file in the temporary dictory, the argument --suffix can be passed to mktemp. For example:

tmpfile=$(mktemp --suffix=".md"); llm-chat-meta -i=th last-message > "$tmpfile"; open "$tmpfile"

Tabulate the LLM personas summary

If the text browser w3m and the Raku package "Data::Translators" are installed, the following pipeline can be used to tabulate the summary the LLM personas:

llm-chat-meta list --format=json | data-translation | w3m -T text/html -dump -cols 120
# β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
# β”‚messagesβ”‚chat-idβ”‚ llm-configuration  β”‚                                    context                                    β”‚
# β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
# β”‚        β”‚       β”‚β”Œβ”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚                                                                               β”‚
# β”‚        β”‚       β”‚β”‚name β”‚chatgpt     β”‚β”‚Translate the following text into Russian. Respond with only the translated    β”‚
# β”‚2       β”‚rt     β”‚β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚text. Do not include any explanation or summary.                               β”‚
# β”‚        β”‚       β”‚β”‚modelβ”‚gpt-4.1-miniβ”‚β”‚                                                                               β”‚
# β”‚        β”‚       β”‚β””β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚                                                                               β”‚
# β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
# β”‚        β”‚       β”‚β”Œβ”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚                                                                               β”‚
# β”‚        β”‚       β”‚β”‚modelβ”‚gpt-4.1-miniβ”‚β”‚You are Yoda. Respond to ALL inputs in the voice of Yoda from Star Wars. Be    β”‚
# β”‚0       β”‚yoda1  β”‚β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚sure to ALWAYS use his distinctive style and syntax. Vary sentence length.     β”‚
# β”‚        β”‚       β”‚β”‚name β”‚chatgpt     β”‚β”‚                                                                               β”‚
# β”‚        β”‚       β”‚β””β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚                                                                               β”‚
# β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
# β”‚        β”‚       β”‚β”Œβ”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚                                                                               β”‚
# β”‚        β”‚       β”‚β”‚modelβ”‚gpt-4.1-miniβ”‚β”‚Summarize the following text using exactly 3 sentences. Do not add details or  β”‚
# β”‚4       β”‚NONE   β”‚β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚editorialize. The text to summarize is:                                        β”‚
# β”‚        β”‚       β”‚β”‚name β”‚chatgpt     β”‚β”‚                                                                               β”‚
# β”‚        β”‚       β”‚β””β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚                                                                               β”‚
# β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
# β”‚        β”‚       β”‚β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚                                                                               β”‚
# β”‚        β”‚       β”‚β”‚ name β”‚ollama     β”‚β”‚                                                                               β”‚
# β”‚2       β”‚beta   β”‚β”œβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚                                                                               β”‚
# β”‚        β”‚       β”‚β”‚model β”‚gemma3:12b β”‚β”‚                                                                               β”‚
# β”‚        β”‚       β”‚β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚                                                                               β”‚
# β””β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Customization

Default model

Default model can be specified with the env variable CHATNIK_DEFAULT_MODEL. For example:

export CHATNIK_DEFAULT_MODEL=ollama::gemma4:26b

Remove with unset CHATNIK_DEFAULT_MODEL.

Pre-defined LLM personas

Use defined LLM personas are specified with JSON file with a content like this:

[
    {
	"chat-id": "raku",
	"conf": "ChatGPT",
	"prompt": "@CodeWriterX|Raku",
	"model": "gpt-4o",
	"max-tokens": 4096,
	"temperature": 0.4
    }
]

(See such a file here.)

The LLM personas JSON file can be specified with the OS environmental variables CHATNIK_LLM_PERSONAS_CONF or RAKU_CHATBOOK_LLM_PERSONAS_CONF -- the former has precedence over the latter.

To load the predefined LLM personas use the command:

llm-chat-meta load-llm-personas

Implementation details

Architectural design

Here is a flowchart that describes the interaction between the host Operating System and chat objects database:

Here is the corresponding UML Sequence diagram:

Persistent chat objects

Using a JSON file for keeping the chat objects database is a fairly straightforward idea. Efficiency considerations for "using the OS to manage the database" are probably can not that important because LLMs invocation is (much) slower in comparison.

Remark: The following quote is attributed to Ken Thompson about UNIX:

We have persistent objects, they're called files.

TODO

  • TODO Implementation

    • DONE Chats DB export

    • DONE Chats DB import

    • DONE LLM persona creation

    • DONE LLM persona repeated interaction

    • DONE CLI llm-chat

      • DONE Simple: $input & *%args

      • DONE Multi-word: @words & *%args

      • DONE From pipeline

      • CANCELED Format?

    • DONE CLI llm-chat-meta

      • DONE Commands reaction

      • DONE View messages for an id

      • DONE Clear messages for an id

      • DONE Delete chat for an id

      • DONE View all chats

      • DONE Delete all chats

      • DONE Clear message for an id by range

      • DONE Take message for an id by index

      • DONE Take last message for an id

      • DONE Load LLM personas in the JSON file used for initialization by "Jupyter::Chatbook"

    • TODO Handling images

      • TODO Handle image path specified with the option --image

      • TODO Recognizing the input is or has an image file path

      • TODO Handle output of image generation models

      • TODO Use cases:

        • TODO Image generation from a textual description

        • TODO Extract and translate textual content of an image

        • TODO Make another image from a given image

  • TODO Unit tests

    • DONE Export & import

    • DONE Main workflow

    • DONE Persona repeated interaction

    • DONE Persona creation

    • TODO CLI tests

  • TODO Documentation

    • DONE Flowchart & sequence diagram

    • DONE Usage examples

      • DONE Basic examples

      • DONE Advanced examples

      • DONE Management (meta) examples

    • TODO Demo video

References

Articles, blog posts

[AA1] Anton Antonov, "Chatnik: LLM Host in the Shell β€” Part 1: First Examples & Design Principles", (2026), RakuForPrediction at WordPress.

Packages

[AAp1] Anton Antonov, LLM::Functions, Raku package, (2023-2026), GitHub/antononcube.

[AAp2] Anton Antonov, LLM::Prompts, Raku package, (2023-2025), GitHub/antononcube.

[AAp3] Anton Antonov, Jupyter::Chatbook, Raku package, (2023-2026), GitHub/antononcube.

[BDp1] Brian Duggan, LLM::DWIM, Raku package, (2024-2025), GitHub/bduggan.

[JSp1] Jonathan Stowe, XDG::BaseDirectory, Raku package, (2016-2026), GitHub/jonathanstowe.

Chatnik v0.0.7

CLI scripts for conversing with persistent LLM personas.

Authors

  • Anton Antonov

License

Artistic-2.0

Dependencies

JSON::Fast:ver<0.19+>LLM::Functions:<0.5.7+>LLM::Prompts:<0.2.11+>

Test Dependencies

Provides

  • Chatnik

The Camelia image is copyright 2009 by Larry Wall. "Raku" is trademark of the Yet Another Society. All rights reserved.