Skip to content

Instantly share code, notes, and snippets.

@rauchg
Last active April 27, 2024 10:43
Show Gist options
  • Star 52 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save rauchg/c5f0b1dc245ad95c593de8336aa382ac to your computer and use it in GitHub Desktop.
Save rauchg/c5f0b1dc245ad95c593de8336aa382ac to your computer and use it in GitHub Desktop.
Perplexity CLI in pure shell
#!/usr/bin/env bash
function p() {
jq -n \
--arg content "$*" \
'{
"model": "pplx-7b-online",
"messages": [
{
"role": "system",
"content": "Be precise and concise."
},
{
"role": "user",
"content": $content
}
],
"stream": true
}' | curl --silent \
--request POST \
--url https://api.perplexity.ai/chat/completions \
--header 'accept: application/json' \
--header "authorization: Bearer $PERPLEXITY_API" \
--header 'content-type: application/json' \
--data @- | jq --unbuffered --raw-input -j 'gsub("^data: "; "") | gsub("\r$"; "") | select(. != null and . != "") | fromjson | .choices[0].delta.content'
}
@gniting
Copy link

gniting commented Jan 21, 2024

Two recommendations for improvements:

  1. streamlining the usage of jq by not building json from scratch
  2. drop the use of readline and use jq to directly parse and output the required data
  3. could also use "$@" instead pf "$*", but that level of complication is not needed at this point 🙂
#!/usr/bin/env bash
function send_perplexity_request() {
    local message_json=$(jq -n \
        --arg content "$*" \
        '{
          "model": "pplx-7b-online",
          "messages": [
            {"role": "system", "content": "Be precise and concise."},
            {"role": "user", "content": $content}
          ],
          "stream": true
        }')
    
    curl --silent \
        --request POST \
        --url https://api.perplexity.ai/chat/completions \
        --header 'accept: application/json' \
        --header "authorization: Bearer $PERPLEXITY_API" \
        --header 'content-type: application/json' \
        --data "$message_json" |
        jq -j '.choices[0].delta.content' && echo ""
}

@rauchg
Copy link
Author

rauchg commented Jan 21, 2024

@gniting Thank you! Given Perplexity API per SSE prefixes everything with data: can jq handle that?

@gniting
Copy link

gniting commented Jan 21, 2024

yep... as follows:

curl --silent [CURL STUFF] |
jq -j 'gsub("^data: "; "") | .choices[0].delta.content'

The gsub function can be used to globally substitute the "data:" prefix with an empty string on each line.

@rauchg
Copy link
Author

rauchg commented Jan 21, 2024

Gotcha, it's not there in the original snippet, so I'm curious about how it was working.

@gniting
Copy link

gniting commented Jan 21, 2024

In all fairness, I didn't actually try the updated version against the actual API! 🙂

@algal
Copy link

algal commented Jan 21, 2024

I've made some changes to produce a version that works for me. The changes are as follows. First, saves the request and response in intermediate local variables instead of piping them, so you can debug by just printing them. Second, does not ask the endpoint to stream and take the sixth output (?), but just takes one result. Third, does not just define the function p but also invokes it, so that the resulting script itself can be set to executable and invoked. Fourth, adds a small usage message if it is called without a command line argument.

#!/usr/bin/env bash
if [ "$#" -eq 0 ]; then
    echo "Usage: $(basename $0) promt_to_send_to_perplexity"
    echo ""
    echo "  Requirements: jq must be installed, and PERPLEXITY_API defined"
    exit 1
fi

function p() {
    local json_request
    json_request=$(jq -n \
		--arg content "$*" \
		'{
      "model": "pplx-7b-online",
      "messages": [
        { "role": "system",  "content": "Be precise and concise." },
        { "role": "user",  "content": $content }
      ],
      "stream": false
    }')
    # echo $json_request # uncomment to debug
    
    local json_response
    json_response=$(curl --silent \
		--request POST \
		--url https://api.perplexity.ai/chat/completions \
		--header 'accept: application/json' \
		--header "authorization: Bearer $PERPLEXITY_API" \
		--header 'content-type: application/json' \
		--data "$json_request")
    # echo $json_response  # uncomment to debug

    echo "$json_response" | jq --raw-output .choices[0].message.content
}
p "$*"

@rauchg
Copy link
Author

rauchg commented Jan 22, 2024

Shipped some improvements by @TooTallNate

@nwaughachukwuma
Copy link

Does this work well for both of you?

@rauchg
Copy link
Author

rauchg commented Jan 24, 2024

Yes. @nwaughachukwuma did you run into issues?

@nwaughachukwuma
Copy link

nwaughachukwuma commented Jan 25, 2024

Ok @rauchg. I wanted to be sure it worked well for you both. I modified the code above and used together-api, and wanted to share.

#!/usr/bin/env bash

function together() {
  local body_json=$(jq -n \
    --arg prompt "[INST]$1[/INST]"  \
    '{
      "model": "mistralai/Mixtral-8x7B-Instruct-v0.1",
      "max_tokens": 512,
      "prompt": $prompt,
      "request_type": "language-model-inference",
      "temperature": 0.7,
      "top_p": 0.7,
      "top_k": 50,
      "repetition_penalty": 1,
      "stream_tokens": true,
      "stop": [
        "[/INST]",
        "</s>",
        "<|im_end|>",
        "<|im_start|>"
      ]
    }')

  # Make the API call and process the output line by line
  while IFS= read -r line; do
    if [[ $line == data:* ]]; then
      # Remove the 'data:' prefix
      json_line=${line#data: }

      if echo "$json_line" | jq empty 2> /dev/null; then
        output=$(echo "$json_line" | jq -r '.choices[0].text')
        
        if [[ $output == 'null' ]]; then
          continue
        fi
        printf "%s" "$output"
      else
        continue
      fi
    fi

  done < <(curl --silent \
    -X POST https://api.together.xyz/v1/completions \
    -H 'accept: application/json' \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $TOGETHER_API_KEY" \
    -d "$body_json"
    )

  echo ""
}

## MAIN
together "$1"

@TooTallNate
Copy link

@nwaughachukwuma We modified it to not use read and instead use a singular jq process to parse the output. This ended up being a lot faster.

@Gabriel-Alves-Cunha
Copy link

@nwaughachukwuma We modified it to not use read and instead use a singular jq process to parse the output. This ended up being a lot faster.

@TooTallNate Could you give us the whole script, please 🥺 🙏 ?

@TooTallNate
Copy link

Not sure what you mean. It's up at the top. @rauchg updated the gist already.

@forthrin
Copy link

Is there a version -- or another tool -- that provides CLI access without registration? (Like running as "Unregistered" in a browser)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment