#!/bin/sh
# shee.sh: a shell-based OpenAI-compatible local chat
# Depends on curl, jq, file and mktemp, otherwise fully POSIX-compatible
# (for history/line editing, run it via rlwrap)
# Configurable in-chat and with environment variables
# Created by Luxferre in 2025, released into public domain

# all configuration variables are here

[ -z "$SHEESH_API_ROOT" ] && SHEESH_API_ROOT='https://text.pollinations.ai/openai'
[ -z "$SHEESH_MODEL" ] && SHEESH_MODEL='openai-fast'
[ -z "$SHEESH_TEMP" ] && SHEESH_TEMP='0.6'
[ -z "$SHEESH_SYSPROMPT" ] && SHEESH_SYSPROMPT='You are a helpful assistant.'

histfile="$(mktemp -t furrfu.XXXXXX)"
datafile="$(mktemp -t furrfu.XXXXXX)"
addhistory() {
  role="$1"; shift
  jq -nc --slurpfile h $histfile --arg r "$role" --arg a "$*" '$h[0]+[{"role":$r,"content":$a}]' > ${histfile}.new
  mv ${histfile}.new $histfile
}

prompt() { printf "\e[1;32m>> \e[0m"; }
clearhist() {
  printf '[]' > $histfile
  addhistory system "$SHEESH_SYSPROMPT"
}
startswith() { case $1 in "$2"*) true;; *) false;; esac; }
setsys() {
  SHEESH_SYSPROMPT="$*"
  clearhist
}
setsysfile() { setsys "$(<"$1")"; }
addfile() {
  tmp="$(mktemp -t furrfufile.XXXXXXX)"
  fname="$(basename $1)"
  if startswith "$1" "http*://"; then
    [ -z "$fname" || "$fname" == "/" ] && $fname="${1//\//_}"
    curl -sSLN $1 > $tmp
  else
    cp "$1" $tmp
  fi
  if file -b --mime-encoding $tmp | grep binary; then
    uuencode -m $tmp $fname > ${tmp}.new
    mv ${tmp}.new $tmp
  else
    echo "Contents of ${fname}:" >> ${tmp}.new
    cat $tmp >> ${tmp}.new
    mv ${tmp}.new $tmp
  fi
  jq -nc --slurpfile h $histfile --rawfile a "$tmp" '$h[0]+[{"role":"user","content":$a}]' > ${histfile}.new
  mv ${histfile}.new $histfile
  rm $tmp
}

listmodels() {
  echo 'Available models:'
  curl -sSL ${SHEESH_API_ROOT}/models \
    ${SHEESH_UA:+-H "User-Agent: $SHEESH_UA"} \
    ${SHEESH_REFERER:+-H "Referer: $SHEESH_REFERER"} \
    ${SHEESH_ORIGIN:+-H "Origin: $SHEESH_ORIGIN"} \
    ${OPENAI_API_KEY:+-H "Authorization: Bearer $OPENAI_API_KEY"} \
    ${SHEESH_COOKIES:+-H "Cookie: $SHEESH_COOKIES"} \
  | jq -r '.data[].id' | while read -r modelname; do
    printf '\e[0;36m* %s\e[0m\n' "$modelname"
  done
}

help() {
  printf 'Current settings:\n\n\e[2;37mEndpoint: %s\n' "$SHEESH_API_ROOT"
  printf 'Model: %s\n' "$SHEESH_MODEL"
  printf 'Temperature: %.2f\n' "$SHEESH_TEMP"
  printf 'System prompt: "%s"\e[0m\n\n' "$SHEESH_SYSPROMPT"
  echo 'Available commands:'
  echo
  echo '* /models: list available models'
  echo '* /model: change the current model'
  echo '* /add: add a file to the context'
  echo '* /temp: change the current temperature'
  echo '* /endpoint: change the current API endpoint'
  echo '* /clear: clear the session context'
  echo '* /sys [prompt]: set a system prompt and clear the context'
  echo '* /sysload [file]: load the system prompt from a file and clear the context'
  echo '* /bye: exit the chat immediately'
  echo
  echo 'Available environment variables for configuration:'
  echo
  echo '* OPENAI_API_KEY: API key string (default empty)'
  echo '* SHEESH_API_ROOT: API endpoint (default https://text.pollinations.ai/openai)'
  echo '* SHEESH_MODEL: model ID (default openai-fast)'
  echo '* SHEESH_TEMP: model temperature (default 0.6)'
  echo '* SHEESH_SYSPROMPT: system prompt (default "You are a helpful assistant.") '
  echo '* SHEESH_UA: user agent override (default empty)'
  echo '* SHEESH_REFERER: Referer header value (default empty)'
  echo '* SHEESH_ORIGIN: Origin header value (default empty)'
  echo '* SHEESH_COOKIES: Cookie header value (default empty)'
  echo
}

trap cleanup SIGINT SIGABRT SIGHUP SIGQUIT
cleanup() {
  rm $histfile $datafile
  printf '\nBye!\n'
  exit 0
}

hdrline='----------------------'
title='SHEE.SH v2 by Luxferre'
printf '\e[1;36m%s\n%s\n%s\e[0m\n' "$hdrline" "$title" "$hdrline"
printf '\e[2;37mEndpoint: %s\n' "$SHEESH_API_ROOT"
printf 'Model: %s\n' "$SHEESH_MODEL"
printf 'Temperature: %.2f\n' "$SHEESH_TEMP"
printf '\e[1;36m%s\e[0m\n' "$hdrline"

setsys "$SHEESH_SYSPROMPT"
while prompt && read -r line; do
  [ -z "$line" ] && continue
  [ "$line" == "/bye" ] && break
  [ "$line" == "/clear" ] && clearhist && echo "Session cleared" && continue
  [ "$line" == "/help" ] && help && continue
  [ "$line" == "/models" ] && listmodels && continue
  startswith "$line" '/sys ' && setsys "${line#\/sys }" && echo "System prompt set and session cleared" && continue
  startswith "$line" '/sysload ' && setsysfile "${line#\/sysload }" && echo "System prompt loaded and session cleared" && continue
  startswith "$line" '/temp ' && SHEESH_TEMP="${line#\/temp }" && echo "Temperature set to $SHEESH_TEMP" && continue
  startswith "$line" '/model ' && SHEESH_MODEL="${line#\/model }" && echo "Model set to $SHEESH_MODEL" && continue
  startswith "$line" '/endpoint ' && SHEESH_API_ROOT="${line#\/endpoint }" && echo "API endpoint set to $SHEESH_API_ROOT" && continue
  if startswith "$line" '/add '; then
    filelist="${line#\/add }"
    for f in $filelist; do
      addfile "$f"
      printf "File %s added to the context\n" "$f"
    done
    continue
  fi
  # commands end, normal message processing start
  addhistory user "$line"
  jq -nc --arg m "$SHEESH_MODEL" --arg t "$SHEESH_TEMP" --slurpfile msgs $histfile \
    '{model:$m,stream:true,private:true,messages:$msgs[0],temperature:($t|tonumber)}' > $datafile
  curl -sSLN "${SHEESH_API_ROOT}/chat/completions" \
    -H 'Content-Type: application/json' \
    ${SHEESH_UA:+-H "User-Agent: $SHEESH_UA"} \
    ${SHEESH_REFERER:+-H "Referer: $SHEESH_REFERER"} \
    ${SHEESH_ORIGIN:+-H "Origin: $SHEESH_ORIGIN"} \
    ${OPENAI_API_KEY:+-H "Authorization: Bearer $OPENAI_API_KEY"} \
    ${SHEESH_COOKIES:+-H "Cookie: $SHEESH_COOKIES"} \
    -H 'Accept: text/event-stream' --data-binary "@$datafile" 2>/dev/null | \
  while IFS= read -r chunk; do
      [ -z "$chunk" ] && continue
      data="${chunk#data: }"
      [ "$data" == "[DONE]" ] && break
      delta=''
      startswith "$data" '{' && delta="$(printf '%s' "$data" | jq -rj '.choices[0].delta.content // empty' 2>/dev/null;printf x)"
      delta="${delta%?}"
      [ -n "$delta" ] && printf "\e[0;33m%s\e[0m" "$delta"
      tailbuf="$tailbuf$delta"
  done
  echo
  addhistory assistant "$tailbuf"
  tailbuf=""
  line=''
done
cleanup
