Nushell > Bash scripting

Scripting can be easy or hard depending on what kind of conditions or arguments need to be passed into the function.

It's very simple to make a one argument string and pass it along to your custom llm function. But what if you have a multi-stage workflow that involves piping data and adding a descriptor for the data to give context.

This is a well-known workflow that often beats re-training models and rag when it comes to quick implementations but this workflow can be logically tricky to maintain because you must test for concatenating strings correctly and handling null cases so that your cli tool doesn't end up being useless.

First case: calling the llm function and it interactively prompts me.

llm

prompt>        

Second case: passing in a string argument

llm "hi"

> "Hello! How may I help you"        

3rd case: piping in a string/data into an llm function

"hi" | llm


> "Hello! How may I help you"        

4th case: piping in a string/data and then concatenating with a descriptor string argument

"translate this into german" | llm "yo yo dawg"        

But Why?

One can argue that satisfying the first 2 use cases would qualify our custom llm cli to be a proper working cli, but in actuality, it would be just be another api route middle man. As we find ourselves in more novel use cases where we encounter data blobs that are not very human-readable and don't want to use data tools for exploring the schema, a good first pass through would require a mixture of concatenating data and a description, assuming that the data itself doesn't have any headers and was given to us in a very natural language-y way.

To implement this kind of logic in bash, we can implement this kind of glue.sh:

#!/usr/bin/env bash
# glue – concatenate piped data and/or command-line arguments

# 1. Slurp stdin (if any) into a variable
stdin=$(cat)

# 2. If we got arguments, keep them; otherwise use an empty array
args=("$@")

# 3. If both stdin and arguments are empty, prompt the user
if [[ -z $stdin && ${#args[@]} -eq 0 ]]; then
    read -rp "Nothing to glue. Enter some text: " stdin
fi

# 4. Concatenate: first stdin, then every argument
result="${stdin}${args[*]}"

# 5. Output the final string
printf '%s\n' "$result"        

This would suffice for my use case, however, the llm namespace is an extremely valuable real estate when it comes to the terminal shell completions and the like. What if in the future, I want to implement functions like "llm db" or "llm db edit" to overwrite calls that can be mistaken for concatenating?

To implement this in bash would rapidly expand the script to mangle the logic of arguments with sub function calls. This is where Nushell shines by leaving much of the type system to handle the intermediate difficulty cases of namespace clashes.

To implement the same logic in nushell we can define the llm function as such which calls a previously implemented groq function:

export def llm [adder?:string] {

  if ($in | is-empty) {
    match $adder {
      null => {
        groq (input "prompt> ")
      }
      _ => {
        groq $adder
      }
    }
  } else {
    match $adder {
      null => {
        groq ($in)
      }
      _ => {
        groq ($in + " " + $adder)
      }
    }
  }
}        

This was a simple implementation using an if statement to detect if the pipe value is empty or not allowing the then block to handle case 1 and 2 while the second else block to handle the case 3 and 4. Nushell documentation on this optional args parameter illustrating the syntax use: https://www.nushell.sh/book/custom_commands.html#optional-positional-parameters

The added benefit of building this modular logical llm script, it allows other subcommands to leverage this main function's pipe-able functionality as if it were object inherited and greatly reduces rewriting the same boiler-plate. Maintainability is only handling the error case where the intended namespace clash needs an exception but as you can see, we get a full cli descriptor for llm and its subcommands:

◄ 0s ⋈┈◎ : llm --help
Usage:
  > llm (adder) 

Subcommands:
  llm db (custom) - 
  llm db edit (custom) - 
  llm program (custom) - 

Flags:
  -h, --help: Display the help message for this command

Parameters:
  adder <string>:  (optional)

Input/output types:
  ╭───┬───────┬────────╮
  │ # │ input │ output │
  ├───┼───────┼────────┤
  │ 0 │ any   │ any    │
  ╰───┴───────┴────────╯        

Hopefully, you found this blog entry useful for your own personal tooling.

To view or add a comment, sign in

Explore content categories