#!/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,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