first commit
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user