Skip to content

Commit

Permalink
Merge pull request #46 from mathiasburger/feature/configuration
Browse files Browse the repository at this point in the history
Use openai compatible environment variables for configuration and allow configuration using more specific variables that do not interfere with the openai defaults
  • Loading branch information
thomas-endres-tng authored May 18, 2024
2 parents 652cbe3 + 0641366 commit 494d196
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 36 deletions.
9 changes: 9 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,12 @@ jobs:
- uses: actions/checkout@v3
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
bats-tests:
name: BATS Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install BATS
run: sudo apt-get update && sudo apt-get install -y bats
- name: Run BATS Tests
run: bats --formatter tap test
9 changes: 9 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,12 @@ Resolves #7
```

Furthermore, commits must be signed off according to the [DCO](DCO).

### Testing

Install [bats](https://bats-core.readthedocs.io/en/stable/installation.html).

Run tests:
```
bats --formatter pretty test
```
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,19 @@ To store your API key using macOS keychain, run
security add-generic-password -a "${USER}" -s OPENAI_API_KEY -w "${apiKey}"
```

## Configuration

You can use the following OpenAI compatible environment variables:
* `OPENAI_API_KEY` - Your OpenAI API key
* `OPENAI_API_BASE` - The base URL for the OpenAI API
* `OPENAI_API_VERSION` - The version of the OpenAI API

You can use the more specific environment variables if you do not want to change OpenAI settings globally:
* `PLEASE_OPENAI_API_KEY` - Your OpenAI API key
* `PLEASE_OPENAI_API_BASE` - The base URL for the OpenAI API
* `PLEASE_OPENAI_API_VERSION` - The version of the OpenAI API
* `PLEASE_OPENAI_CHAT_MODEL` - The chat model to use

## Troubleshooting

If you receive the following error message:
Expand Down
77 changes: 41 additions & 36 deletions please.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

set -uo pipefail

model='gpt-4'
model=${PLEASE_OPENAI_CHAT_MODEL:-'gpt-4-turbo'}
options=("[I] Invoke" "[C] Copy to clipboard" "[Q] Ask a question" "[A] Abort" )
number_of_options=${#options[@]}
keyName="OPENAI_API_KEY"
Expand All @@ -22,7 +22,10 @@ exclamation="\xE2\x9D\x97"
questionMark="\x1B[31m?\x1B[0m"
checkMark="\x1B[31m\xE2\x9C\x93\x1B[0m"

openai_invocation_url=${OPENAI_URL:-"https://api.openai.com/v1"}
openai_api_base=${PLEASE_OPENAI_API_BASE:-${OPENAI_API_BASE:-${OPENAI_URL:-"https://api.openai.com"}}}
openai_api_version=${PLEASE_OPENAI_API_VERSION:-${OPENAI_API_VERSION:-"v1"}}
openai_invocation_url=${openai_api_base}/${openai_api_version}

fail_msg="echo 'I do not know. Please rephrase your question.'"

declare -a qaMessages=()
Expand Down Expand Up @@ -98,10 +101,10 @@ function store_api_key() {
else
if [[ "$OSTYPE" == "darwin"* ]]; then
security add-generic-password -a "${USER}" -s "${keyName}" -w "${apiKey}" -U
OPENAI_API_KEY=$(security find-generic-password -a "${USER}" -s "${keyName}" -w)
PLEASE_OPENAI_API_KEY=$(security find-generic-password -a "${USER}" -s "${keyName}" -w)
else
echo -e "${apiKey}" | secret-tool store --label="${keyName}" username "${USER}" key_name "${keyName}"
OPENAI_API_KEY=$(secret-tool lookup username "${USER}" key_name "${keyName}")
PLEASE_OPENAI_API_KEY=$(secret-tool lookup username "${USER}" key_name "${keyName}")
fi
echo "API key stored successfully and set as a global variable."
break
Expand Down Expand Up @@ -130,7 +133,7 @@ display_help() {
echo " The remaining arguments are used as input to be turned into a CLI command."
echo
echo "OpenAI API Key:"
echo " The API key needs to be set as OPENAI_API_KEY environment variable or keychain entry. "
echo " The API key needs to be set as PLEASE_OPENAI_API_KEY or OPENAI_API_KEY environment variable or keychain entry. "
}


Expand All @@ -141,8 +144,8 @@ debug() {
}

check_key() {
if [ -z "${OPENAI_API_KEY+x}" ]; then
debug "OPENAI_API_KEY environment variable not set, trying to find it in keychain"
if [ -z "${PLEASE_OPENAI_API_KEY:-${OPENAI_API_KEY:-}}" ]; then
debug "PLEASE_OPENAI_API_KEY or OPENAI_API_KEY environment variable not set, trying to find it in keychain"
get_key_from_keychain
fi
}
Expand All @@ -156,21 +159,21 @@ get_key_from_keychain() {
;;
Linux*)
if ! command -v secret-tool &> /dev/null; then
debug "OPENAI_API_KEY not set and secret-tool not installed. Install it with 'sudo apt install libsecret-tools'."
debug "PLEASE_OPENAI_API_KEY or OPENAI_API_KEY not set and secret-tool not installed. Install it with 'sudo apt install libsecret-tools'."
exitStatus=1
else
key=$(secret-tool lookup username "${USER}" key_name "${keyName}")
exitStatus=$?
fi
;;
*)
debug "OPENAI_API_KEY not set and no supported keychain available."
debug "PLEASE_OPENAI_API_KEY or OPENAI_API_KEY not set and no supported keychain available."
exitStatus=1
;;
esac

if [ "${exitStatus}" -ne 0 ]; then
echo "OPENAI_API_KEY not set and unable to find it in keychain. See the README on how to persist your key."
echo "PLEASE_OPENAI_API_KEY or OPENAI_API_KEY not set and unable to find it in keychain. See the README on how to persist your key."
echo "You can get an API at https://beta.openai.com/"
echo "Please enter your OpenAI API key now to use it this time only, or rerun 'please -a' to store it in the keychain."
echo "Your API Key:"
Expand All @@ -181,7 +184,7 @@ get_key_from_keychain() {
echo "No API key provided. Exiting."
exit 1
fi
OPENAI_API_KEY="${key}"
PLEASE_OPENAI_API_KEY="${key}"
}

get_command() {
Expand Down Expand Up @@ -221,7 +224,7 @@ perform_openai_request() {
-s -w "\n%{http_code}" \
-H "Content-Type: application/json" \
-H "Accept-Encoding: identity" \
-H "Authorization: Bearer ${OPENAI_API_KEY}" \
-H "Authorization: Bearer ${PLEASE_OPENAI_API_KEY:-$OPENAI_API_KEY}" \
-d "${payload}" \
--silent)
debug "Response:\n${result[*]}"
Expand Down Expand Up @@ -433,12 +436,7 @@ answer_question_about_command() {
prompt="${question}"
escapedPrompt=$(printf %s "${prompt}" | jq -srR '@json')
qaMessages+=("{ \"role\": \"user\", \"content\": ${escapedPrompt} }")

if [ -z "${qaMessages+x}" ]; then
messagesJson="[]"
else
messagesJson='['$(join_by , "${qaMessages[@]}")']'
fi
messagesJson='['$(join_by , "${qaMessages[@]}")']'

payload=$(jq --null-input --compact-output --argjson messagesJson "${messagesJson}" '{
max_tokens: 200,
Expand All @@ -460,26 +458,33 @@ function join_by {
fi
}

if [ $# -eq 0 ]; then
input=("-h")
else
input=("$@")
fi
function main() {
if [ $# -eq 0 ]; then
input=("-h")
else
input=("$@")
fi

check_args "${input[@]}"
check_key

check_args "${input[@]}"
check_key
get_command
if [ "${explain}" -eq 1 ]; then
explain_command
fi

get_command
if [ "${explain}" -eq 1 ]; then
explain_command
fi
print_option

print_option
if test "${command}" = "${fail_msg}"; then
exit 1
fi

if test "${command}" = "${fail_msg}"; then
exit 1
fi
init_questions
choose_action
act_on_action
}

init_questions
choose_action
act_on_action
# Only call main if the script is not being sourced
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi
49 changes: 49 additions & 0 deletions test/join_by.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env bats

load $BATS_TEST_DIRNAME/../please.sh

@test "join elements with a comma" {
result=$(join_by ',' 'a' 'b' 'c')
[ "$result" == "a,b,c" ]
}

@test "join elements with a semicolon" {
result=$(join_by ';' '1' '2' '3')
[ "$result" == "1;2;3" ]
}

@test "join with a space as delimiter" {
result=$(join_by ' ' 'x' 'y' 'z')
[ "$result" == "x y z" ]
}

@test "join with an empty delimiter" {
result=$(join_by '' '1' '2' '3')
[ "$result" == "123" ]
}

@test "no delimiter provided should return the first element" {
result=$(join_by '' 'single')
[ "$result" == "single" ]
}

@test "join using array with no elements" {
qaMessages=()

result=$(join_by , "${qaMessages[@]}")
[ "$result" == "" ]
}

@test "join using array with one element" {
qaMessages=("a")

result=$(join_by , "${qaMessages[@]}")
[ "$result" == "a" ]
}

@test "join using array with two elements" {
qaMessages=("a" "b")

result=$(join_by , "${qaMessages[@]}")
[ "$result" == "a,b" ]
}
13 changes: 13 additions & 0 deletions test/main.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bats

PLEASE_EXE=$BATS_TEST_DIRNAME/../please.sh

@test "smoke test please --help" {
result=$($PLEASE_EXE '--help')
[ "${result:0:6}" == "Please" ]
}

@test "smoke test please --version" {
result=$($PLEASE_EXE '--version')
[ "${result:0:8}" == "Please v" ]
}

0 comments on commit 494d196

Please sign in to comment.