ChatGPT als Helfer in der Shell

Anf├Ąnger10 Apr. 2023ChatGPTZ-ShellBashShell
ChatGPT ist schon ohnehin ein sehr m├Ąchtiges Werkzeug, ├╝ber die Integration in die Kommandozeile, k├Ânnen wir es aber noch einen Ticken effektiver nutzen. In einer Schritt-f├╝r-Schritt Anleitung erstellen wir drei Scripte, die direkt die ChatGPT API in die zsh Shell einbinden. Und das (fast) ohne zus├Ątzliche Bibliotheken und mit nur wenigen Zeilen Code.
Noch keine Stimmen abgegeben
Noch keine Kommentare

Dieser Artikel basiert auf dem Blogpost "become a 1000x engineer or die tryin" (https://kadekillary.work/posts/1000x-eng/)" in dem die Anbindung der OpenAi API mit der Fish Shell aufgezeigt wurde. In diesem Artikel implementieren wir die Beispiele mit der Zsh Shell nach. Und es war tats├Ąchlich etwas mehr Arbeit als nur ChatGPT danach h├Âflich zu fragen die Skripte umzuschreiben.

Voraussetzung f├╝r die Implementierung ist ein OpenAi API-Key und die jq-Bibliothek f├╝r JSON. Ersteres k├Ânnen wir mit einem g├╝ltigen OpenAi Account einfach von der Webseite bekommen. Die jq-Library muss von https://stedolan.github.io/jq/ heruntergeladen und in den PATH eingef├╝gt werden, zB. f├╝r zsh (oder bash) in der .zshrc Datei:

export PATH=$PATH:/Users/matthias/Apps/apache-maven/bin:/Users/matthias/Apps/jq-osx-amd64
export OPENAI_KEY=XXXXX
# Wie man sieht, habe ich neben jq auch noch maven im "Path".

Die "jq" Datei muss auch mit "chmod +x" ausf├╝hrbar geamcht werden.

Hinweis zum Bearbeiten der "dot"-Dateien in OSX: Die Dateien sind im Finder per default nicht sichtbar (also auch, wenn man sie in einem Texteditor ├Âffnen m├Âchte). ├ťber die Tastenkobination "Ôîś cmd + Ôçž shift + ." k├Ânnen wir sie aber einblenden.

Jetzt k├Ânnen wir auch schon gleich mit dem ersten Script loslegen:

function gpt_ask() {
  local prompt="'$(echo "$*" | sed "s/'/\\\\'/g")'"
  local gpt=$(curl https://api.openai.com/v1/chat/completions -s \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $OPENAI_KEY" \
    -d '{
        "model": "gpt-3.5-turbo",
        "messages": [{"role": "user","content": "'"$prompt"'"}],
        "temperature": 0.7,
        "stream": true
        }')

  while read -r text; do
    # "data: [DONE]" markiert das Ende des Outputs
    if [[ $text == "data: [DONE]" ]]; then
      break
    # Mit "role" startet die Ausgabe
    elif [[ $text =~ role ]]; then
      continue
    # Alles was "content" ist, beinhaltet die Antwort
    elif [[ $text =~ content ]]; then
      # Das Schl├╝sselwort "data: " ist dem eigentlichen JSON Response vorangestellt - weg damit
      text=${text#"data: "}
      # Mit jq k├Ânnen wir die Ausgabe elegant extrahieren und ├╝ber echo (mit dem E Parameter) oder print ausgeben
      #printf "%s" "$text" | jq -r -j '.choices[0].delta.content'
      echo -E "$text" | jq -r -j '.choices[0].delta.content'
    else
      continue
    fi
  done <<< "$gpt"
}

Wir machen mit curl ein Request zu der OpenAi API, dabei m├╝ssen wir nat├╝rlich unseren API Key als Barer Token angeben und konfigurieren, welches Model und mit welchen Parametern verwenden wird: Neben dem "gpt-3.5-turbo" gibt es noch eine Reihe weiterer Modele/Optionen, bitte am besten hierf├╝r die Dokumentation konsultieren (https://platform.openai.com/docs/api-reference/models).

Die Antwort von der API kommt als JSON-String, es interessieren uns hier nur Knoten, die die Elemente "choices", "delte" und "content" beinhalten. Das Element "data: DONE" markiert das Ende des Streams. Mit "jq" extrahieren wir die einzelnen Bestandteile des Responses und geben sie ├╝ber "echo" in der Konsole zur├╝ck. Das war es schon! Jetzt nur noch ├╝ber "source .zshrc" das Environment wieder einlesen und wir k├Ânnen gleich loslegen:

gpt_ask "What is the latest Version of Java?"
As an AI language model, I don't have access to the latest information on the internet. However, as of August 2021, the latest stable version of Java is Java 16.0.2

Noch interessanter ist die Nutzung von ChatGPT, wenn wir neben unserer Anfrage auch noch Daten mitschicken k├Ânnen:

function gpt_data() {
  curl https://api.openai.com/v1/chat/completions -s \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_KEY" \
  -d '{
    "model": "gpt-3.5-turbo",
    "messages": [{"role": "user","content": "'"$1: $(echo -n "$2" | sed 's/$/\\n/g' | tr -d '\n')"'"}],
    "temperature": 0.7
    }' | jq -r '.choices[0].message.content'
}

Das Script ist fast identisch mit dem vorherigen, der wesentliche Unterschied ist, dass wir jetzt eben auch zus├Ątzlich zu dem "Prompt" Daten senden (Der Parameter $1 ist die "prompt" Eingabe, mit $2 kommen die Daten). Die Daten werden samt des Prompt-Inputs im Feld "content" im JSON Body des Requests geschickt. Das hat zur Folge, dass Sonderzeichen, vor allem der Zeilenumbruch, escaped werden m├╝ssen (sed 's/$/\\n/g' | tr -d '\n' ). Im Fall einer CSV Datei funktioniert das recht gut, jedoch wird es hier sicher paar F├Ąlle geben, wo das Script nicht funktioniert.

Hier exemplarischer Anwendugnsfall: Zuerst beschaffen wir uns mit "gpt_ask" Daten der Aktienkurse einer fiktiven Firma aus den letzen 100 Tage und speichern das in einer csv Datei:

gpt_ask "Create a csv with the imaginary stock data for 100 days containing the columns date, open and close - please include only the data, nothing else" > stock_data.csv

Und jetzt k├Ânnen wir die Daten mit ChatGPT auswerten:

$gpt_data "What was the average Open-Price?" "$(cat stock_data.csv)"
The average Open-Price is 272.5.

Mit "$(cat stock_data.csv)" inkludieren wir die davor generierten Daten.

Als letztes Beispiel nutzen wir den "Image"-Endpoint zur Generierung von Bildern:

function gpt_image() {
  local prompt="$1"
  local gpt=$(curl https://api.openai.com/v1/images/generations -s \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_KEY" \
  -d '{
      "n": 1,
      "prompt": "'"$prompt"'",
      "size": "1024x1024"
      }')
  echo $gpt
  local url=$(echo $gpt | jq -r '.data[0].url')

  local filename=$(echo "$prompt" | tr ' ' '_')
  filename=$(echo "$filename" | tr -cd '[:alnum:]_-')

  fname_length=${#filename}
  if [[ $fname_length -gt 150 ]]; then
    filename=${filename:0:150}
  fi

  curl -s $url -o img-"$filename".png
}

Der Aufruf der OpenAi API mit curl ist ├Ąhnlich wie schon bei den vorherigen Beispielen. Nat├╝rlich haben wir hier die spieziellen Parameter f├╝r die Bildgenerierung:

  • "n": Anzahl der zu generierenden Bildern (wir nehmen nur 1, weil wir das Bild auch sofort speichern m├Âchten)
  • "size", wobei mit "1024x1024" hier auch schon die im Moment verf├╝gbare maximal Aufl├Âsung gew├Ąhlt ist.

Wir versuchen in dem Script das Bild zu speichern, als Bildname wird die prompt-Eingabe verwendet (dabei bereinigen wir auch Sonderzeichen mit tr -cd '[:alnum:]_-' ) und die maximale L├Ąnge des Namens ist auf 150 Zeichen begrenzt.

Kleiner Test:

gpt_image "Happy code monkey"

JBerries mit ChatGPT - Happy code monkeyJBerries - Happy code monkey