From 321f58d71f3299ca669d5a8a1c99d6fad38ea5da Mon Sep 17 00:00:00 2001 From: dahromy Date: Mon, 16 Sep 2024 17:22:27 +0300 Subject: [PATCH 1/3] feat: Add option to select AI provider for zsh-copilot --- zsh-copilot.plugin.zsh | 81 +++++++++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 32 deletions(-) diff --git a/zsh-copilot.plugin.zsh b/zsh-copilot.plugin.zsh index 2a73842..72794ee 100644 --- a/zsh-copilot.plugin.zsh +++ b/zsh-copilot.plugin.zsh @@ -1,15 +1,19 @@ +# Default key binding (( ! ${+ZSH_COPILOT_KEY} )) && typeset -g ZSH_COPILOT_KEY='^z' +# Configuration options (( ! ${+ZSH_COPILOT_SEND_CONTEXT} )) && typeset -g ZSH_COPILOT_SEND_CONTEXT=true -# (( ! ${+ZSH_COPILOT_SEND_GIT_DIFF} )) && -# typeset -g ZSH_COPILOT_SEND_GIT_DIFF=true - (( ! ${+ZSH_COPILOT_DEBUG} )) && typeset -g ZSH_COPILOT_DEBUG=false +# New option to select AI provider +(( ! ${+ZSH_COPILOT_AI_PROVIDER} )) && + typeset -g ZSH_COPILOT_AI_PROVIDER="openai" + +# System prompt read -r -d '' SYSTEM_PROMPT <<- EOM You will be given the raw input of a shell command. Your task is to either complete the command or provide a new command that you think the user is trying to type. @@ -41,13 +45,15 @@ fi function _suggest_ai() { local OPENAI_API_URL=${OPENAI_API_URL:-"api.openai.com"} + local ANTHROPIC_API_URL=${ANTHROPIC_API_URL:-"api.anthropic.com"} + local context_info="" if [[ "$ZSH_COPILOT_SEND_CONTEXT" == 'true' ]]; then - local PROMPT="$SYSTEM_PROMPT - Context: You are user $(whoami) with id $(id) in directory $(pwd). + context_info="Context: You are user $(whoami) with id $(id) in directory $(pwd). Your shell is $(echo $SHELL) and your terminal is $(echo $TERM) running on $(uname -a). $SYSTEM" fi + # Get input local input=$(echo "${BUFFER:0:$CURSOR}" | tr '\n' ';') input=$(echo "$input" | sed 's/"/\\"/g') @@ -55,26 +61,18 @@ function _suggest_ai() { _zsh_autosuggest_clear zle -R "Thinking..." - PROMPT=$(echo "$PROMPT" | tr -d '\n') - # Wasn't able to get this to work :( - # if [[ "$ZSH_COPILOT_SEND_GIT_DIFF" == 'true' ]]; then - # if [[ $(git rev-parse --is-inside-work-tree) == 'true' ]]; then - # local git_diff=$(git diff --staged --no-color) - # local git_exit_code=$? - # git_diff=$(echo "$git_diff" | tr '\\' ' ' | sed 's/[\$\"\`]/\\&/g' | tr '\\' '\\\\' | tr -d '\n') - # - # if [[ git_exit_code -eq 0 ]]; then - # PROMPT="$PROMPT; This is the git diff: <---->$git_diff<----> You may provide a git commit message if the user is trying to commit changes. You are an expert at committing changes, you don't give generic messages. You give the best commit messages" - # fi - # fi - # fi + local full_prompt=$(echo "$SYSTEM_PROMPT $context_info" | tr -d '\n') - local data="{ - \"model\": \"gpt-4\", + local data + local response + + if [[ "$ZSH_COPILOT_AI_PROVIDER" == "openai" ]]; then + data="{ + \"model\": \"gpt-4o-mini\", \"messages\": [ { \"role\": \"system\", - \"content\": \"$PROMPT\" + \"content\": \"$full_prompt\" }, { \"role\": \"user\", @@ -82,14 +80,35 @@ function _suggest_ai() { } ] }" - local response=$(curl "https://${OPENAI_API_URL}/v1/chat/completions" \ - --silent \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer $OPENAI_API_KEY" \ - -d $data) - local message=$(echo "$response" | jq -r '.choices[0].message.content') - - # zle -U "$suggestion" + response=$(curl "https://${OPENAI_API_URL}/v1/chat/completions" \ + --silent \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $OPENAI_API_KEY" \ + -d "$data") + local message=$(echo "$response" | jq -r '.choices[0].message.content') + elif [[ "$ZSH_COPILOT_AI_PROVIDER" == "anthropic" ]]; then + data="{ + \"model\": \"claude-3-5-sonnet-20240620\", + \"max_tokens\": 1000, + \"system\": \"$full_prompt\", + \"messages\": [ + { + \"role\": \"user\", + \"content\": \"$input\" + } + ] + }" + response=$(curl "https://${ANTHROPIC_API_URL}/v1/messages" \ + --silent \ + -H "Content-Type: application/json" \ + -H "x-api-key: $ANTHROPIC_API_KEY" \ + -H "anthropic-version: 2023-06-01" \ + -d "$data") + local message=$(echo "$response" | jq -r '.content[0].text') + else + echo "Invalid AI provider selected. Please choose 'openai' or 'anthropic'." + return 1 + fi local first_char=${message:0:1} local suggestion=${message:1:${#message}} @@ -107,7 +126,6 @@ function _suggest_ai() { zle -U "$suggestion" elif [[ "$first_char" == '+' ]]; then _zsh_autosuggest_suggest "$suggestion" - # POSTDISPLAY="$suggestion" fi } @@ -117,9 +135,8 @@ function zsh-copilot() { echo "Configurations:" echo " - ZSH_COPILOT_KEY: Key to press to get suggestions (default: ^z, value: $ZSH_COPILOT_KEY)." echo " - ZSH_COPILOT_SEND_CONTEXT: If \`true\`, zsh-copilot will send context information (whoami, shell, pwd, etc.) to the AI model (default: true, value: $ZSH_COPILOT_SEND_CONTEXT)." - # echo " - ZSH_COPILOT_SEND_GIT_DIFF: If \`true\`, zsh-copilot will send the git diff (if available) to the AI model (default: true, value: $ZSH_COPILOT_SEND_GIT_DIFF)." + echo " - ZSH_COPILOT_AI_PROVIDER: AI provider to use ('openai' or 'anthropic', default: openai, value: $ZSH_COPILOT_AI_PROVIDER)." } zle -N _suggest_ai bindkey $ZSH_COPILOT_KEY _suggest_ai - From ee547607ec5bf97340e10311d21b53df2ffa86d1 Mon Sep 17 00:00:00 2001 From: "dahromy (aider)" Date: Mon, 16 Sep 2024 17:24:42 +0300 Subject: [PATCH 2/3] docs: Update README.md with new AI provider configuration --- README.md | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 51fcc4f..ebf0655 100644 --- a/README.md +++ b/README.md @@ -22,18 +22,34 @@ echo "source ~/.zsh-copilot/zsh-copilot.plugin.zsh" >> ~/.zshrc ### Configuration -You need to have an OPENAI API key with access to `gpt-4` to use this plugin. Expose this via the `OPENAI_API_KEY` environment variable: +You need to have an API key for either OpenAI or Anthropic to use this plugin. Expose this via the appropriate environment variable: +For OpenAI (default): ```sh -export OPENAI_API_KEY= +export OPENAI_API_KEY= ``` -I tried out using `gpt-3` but the results were garbage. +For Anthropic: +```sh +export ANTHROPIC_API_KEY= +``` -To see available configurations, run: +You can configure the AI provider using the `ZSH_COPILOT_AI_PROVIDER` variable: ```sh -zsh-copilot --help +export ZSH_COPILOT_AI_PROVIDER="openai" # or "anthropic" +``` + +Other configuration options: + +- `ZSH_COPILOT_KEY`: Key to press to get suggestions (default: ^z) +- `ZSH_COPILOT_SEND_CONTEXT`: If `true`, zsh-copilot will send context information to the AI model (default: true) +- `ZSH_COPILOT_DEBUG`: Enable debug logging (default: false) + +To see all available configurations and their current values, run: + +```sh +zsh-copilot ``` ## Usage From 9b7ee410d0d6b75916691d948fe12c7c443bfd14 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 18 Sep 2024 18:45:05 +0200 Subject: [PATCH 3/3] fix: Improve prompt --- zsh-copilot.plugin.zsh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zsh-copilot.plugin.zsh b/zsh-copilot.plugin.zsh index 72794ee..e73637a 100644 --- a/zsh-copilot.plugin.zsh +++ b/zsh-copilot.plugin.zsh @@ -33,8 +33,8 @@ read -r -d '' SYSTEM_PROMPT <<- EOM DO NOT INTERACT WITH THE USER IN NATURAL LANGUAGE! If you do, you will be banned from the system. Note that the double quote sign is escaped. Keep this in mind when you create quotes. Here are two examples: - * User input: 'list files in current directory'; Your response: '=ls # ls is the builtin command for listing files' - * User input: 'cd /tm'; Your response: '+p # /tmp is the standard temp folder on linux and mac'. + * User input: 'list files in current directory'; Your response: '=ls' (ls is the builtin command for listing files) + * User input: 'cd /tm'; Your response: '+p' (/tmp is the standard temp folder on linux and mac). EOM if [[ "$OSTYPE" == "darwin"* ]]; then