Last active
October 25, 2024 05:40
-
-
Save hizkifw/86c941c5a8e289b7429a7e2fa4807571 to your computer and use it in GitHub Desktop.
Invoke LLMs from the command line
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# | |
# ai.sh - invoke llm from command line | |
# | |
# Usage: | |
# * Run interactively | |
# ai.sh | |
# | |
# * Invoke a single inference from command line | |
# ai.sh ffmpeg convert all mp4 in this folder to mp3 | |
# | |
# * Inject text content into the context | |
# cat document.txt | ai summarize this document | |
# | |
# * Inject text content into interactive session | |
# cat document.txt | ai | |
# | |
aish_openai_endpoint='https://openrouter.ai/api/v1' | |
aish_openai_key='sk-or-v1-xxxxxxxxxxxxxxxx' | |
aish_openai_model='anthropic/claude-3.5-sonnet' | |
system='You are an assistant invoked through a command line. Generate a code | |
snippet to fulfill the task. By default: only output the command, no explanations, | |
unless otherwise asked by user. If asked to explain, format responses such that | |
it looks good in the CLI, e.g. hard wrap to 80 characters, and be concise.' | |
require() { | |
cmd="$1" | |
if ! command -v "$cmd" >/dev/null; then | |
echo "$cmd not installed" | |
exit 1 | |
fi | |
} | |
require curl | |
require jq | |
# Attach stdin if it exists | |
prompt="$@" | |
interactive=false | |
extra_content='' | |
if [[ ! -t 0 ]]; then | |
stdin=`cat` | |
extra_content="[stdin after this line]\n$stdin" | |
nchars=$(wc -c <<< "$stdin") | |
echo "[*] Loaded $nchars bytes from stdin into context" | |
fi | |
messages=$(jq -n \ | |
--arg system "$system" \ | |
--arg extra "$extra_content" \ | |
'[ | |
{ role: "system", content: $system }, | |
(if $extra != "" then | |
{ role: "user", content: $extra } | |
else | |
empty | |
end) | |
]' | |
) | |
add_message() { | |
role="$1" | |
content="$2" | |
messages=$(jq \ | |
--arg role "$role" \ | |
--arg content "$content" \ | |
'. + [ { role: $role, content: $content } ]' \ | |
<<< "$messages" | |
) | |
} | |
inference() { | |
# Prompt user if there is no prompt from the arguments | |
if [[ -z "$prompt" ]]; then | |
interactive=true | |
IFS= read -r -p "> " prompt < /dev/tty || { | |
echo '^D' | |
exit 0 | |
} | |
fi | |
# Update context | |
add_message user "$prompt" | |
# Construct request object | |
req_json=$(jq -n \ | |
--arg model "$aish_openai_model" \ | |
--argjson messages "$messages" \ | |
'{ | |
model: $model, | |
messages: $messages, | |
stream: true | |
}' | |
) | |
# Send streaming request | |
curl -fsSLN "$aish_openai_endpoint/chat/completions" \ | |
-XPOST \ | |
-H "Content-Type: application/json" \ | |
-H "Authorization: Bearer $aish_openai_key" \ | |
-d "$req_json" \ | |
| grep --line-buffered -E '^data: ' \ | |
| grep --line-buffered -v '[[DONE]]' \ | |
| stdbuf -oL cut -c 7- \ | |
| jq --unbuffered -j '.choices[0].delta.content' \ | |
| tee >/dev/stderr | read -r response | |
echo '' | |
# Update context | |
add_message assistant "$response" | |
# Prepare for next loop if any | |
prompt='' | |
} | |
inference | |
while [ "$interactive" = true ]; do | |
inference | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment