Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing personalization with Gorilla #56

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ dist
.vscode
.idea
.editorconfig
.env
148 changes: 146 additions & 2 deletions go_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,29 @@
import fcntl
import platform
import requests
from openai import OpenAI
import json
import subprocess
import argparse
import termios
import urllib.parse
import sys
from halo import Halo
import go_questionary

__version__ = "0.0.11" # current version
from utils import personalize
__version__ = "0.0.12" # current version
SERVER_URL = "https://cli.gorilla-llm.com"
UPDATE_CHECK_FILE = os.path.expanduser("~/.gorilla-cli-last-update-check")
USERID_FILE = os.path.expanduser("~/.gorilla-cli-userid")
HISTORY_FILE = os.path.expanduser("~/.gorilla_cli_history")
CONFIG_FILE = os.path.expanduser("~/.gorilla-cli-config.json")
ISSUE_URL = f"https://github.com/gorilla-llm/gorilla-cli/issues/new"
GORILLA_EMOJI = "🦍 " if go_questionary.try_encode_gorilla() else ""
HISTORY_LENGTH = 10
WELCOME_TEXT = f"""===***===
{GORILLA_EMOJI}Welcome to Gorilla-CLI! Enhance your Command Line with the power of LLMs!


Simply use `gorilla <your desired operation>` and Gorilla will do the rest. For instance:
gorilla generate 100 random characters into a file called test.txt
gorilla get the image ids of all pods running in all namespaces in kubernetes
Expand All @@ -54,6 +58,13 @@
def generate_random_uid():
return str(uuid.uuid4())

def load_config():
arthbohra marked this conversation as resolved.
Show resolved Hide resolved
# Load the user's configuration file and perform any necessary checks
if os.path.isfile(CONFIG_FILE):
with open(CONFIG_FILE, "r") as config_file:
config_json = json.load(config_file)
return config_json

def get_git_email():
return subprocess.check_output(["git", "config", "--global", "user.email"]).decode("utf-8").strip()

Expand Down Expand Up @@ -157,6 +168,15 @@ def get_user_id():
print(f"Using a temporary UID {user_id} for now.")
return user_id

def request_personalization():
# ask the user if they want to personalize their bash history - depending on the Y/n response, set the personalize flag to true/false
response = input("Do you want to personalize your bash history? [Y/n]: ").strip().lower()
if response in ["n", "no"]:
print("We won't use your bash history to personalize your queries. You can always turn this feature on in the future!")
editPersonalizationSettings(False)
print("We're going to be using your bash history to personalize your queries. This feature will require OpenAI API access, so enter your API key when prompted below. You can always turn this feature off in the future!")
editPersonalizationSettings(True)

def format_command(input_str):
"""
Standardize commands to be stored with a newline
Expand All @@ -183,6 +203,95 @@ def append_string_to_file_if_missing(file_path, target_string):
with open(file_path, 'w') as file:
file.write(target_string)

def checkOpenAIAPIValidity(openai_key: str):
try:
client = OpenAI(api_key=openai_key)
client.models.list()
except Exception as e:
return False
return True

def changeOpenAIKey():
new_key = str(input("Enter your new OpenAI API key: ")).strip()
while not checkOpenAIAPIValidity(new_key):
response = str(input("The API key you entered is invalid. Do you want to try again? [Y/n]: ")).strip().lower()
if response in ["n", "no"]:
return False
else:
new_key = str(input("Enter your new OpenAI API key: ")).strip()
return new_key

# this allows you to edit your personalization settings - optional variable is the key for open ai
def editPersonalizationSettings(permission: bool):
if os.path.isfile(CONFIG_FILE):
with open(CONFIG_FILE, "r") as config_file:
try:
config_json = json.load(config_file)
except:
config_json = {}
if "personalization" in config_json:
if config_json["personalization"]["permission"] and permission:
current_key = config_json["personalization"]["api_key"]
res = input(f"You are already using the the following API key:\n\n{current_key}\n\nDo you want to change it?").strip().lower()
if res in ["n", "no"]:
print ("You're all set.")
else:
api_key = changeOpenAIKey()
if api_key:
print ("We successfully updated your API key.")
config_json["personalization"]["permission"] = True
config_json["personalization"]["api_key"] = api_key

else:
print("You didn't provide a valid API key, so we didn't update your settings.")
elif not config_json["personalization"]["permission"] and not permission:
print ("You already have personalization disabled. You're good to go!")
elif config_json["personalization"]["permission"] and not permission:
print ("We turned off personalization for you.")
config_json["personalization"]["permission"] = False
config_json["personalization"]["api_key"] = None
else:
api_key = changeOpenAIKey()
if api_key:
print ("We successfully added your API key.")
config_json["personalization"]["api_key"] = api_key
config_json["personalization"]["permission"] = True
else:
print("You didn't provide a valid API key, so we didn't update your settings.")
config_json["personalization"]["permission"] = False
config_json["personalization"]["api_key"] = None
else:
if permission:
api_key = changeOpenAIKey()
if api_key:
print ("We successfully added your API key.")
config_json["personalization"] = {"permission": True, "api_key": api_key}
else:
print("You didn't provide a valid API key, so we didn't update your settings.")
config_json["personalization"] = {"permission": False, "api_key": None}
else:
print ("We turned off personalization for you.")
config_json["personalization"] = {"permission": False, "api_key": None}
else:
if permission:
api_key = changeOpenAIKey()
if api_key:
print ("We successfully added your API key.")
config_json = {"personalization": {"permission": True, "api_key": api_key}}
else:
print("You didn't provide a valid API key, so we didn't update your settings.")
config_json = {"personalization": {"permission": False, "api_key": None}}
else:
print ("We turned off personalization for you.")
config_json = {"personalization": {"permission": False, "api_key": None}}

with open(CONFIG_FILE, "w") as config_file:
json.dump(config_json, config_file)






def main():
def execute_command(cmd):
Expand Down Expand Up @@ -219,6 +328,9 @@ def get_history_commands(history_file):
user_input = " ".join(args)
user_id = get_user_id()
system_info = get_system_info()
personalization = False
open_ai_key = None
personalized_history = None


# Parse command-line arguments
Expand All @@ -231,13 +343,45 @@ def get_history_commands(history_file):
# Generate a unique interaction ID
interaction_id = str(uuid.uuid4())

request_personalization()
# open the config file and check if the user has personalized their bash history
if os.path.isfile(CONFIG_FILE):
with open(CONFIG_FILE, "r") as config_file:
try:
config_json = json.load(config_file)
print (config_json)
except:
request_personalization()
config_json = json.load(config_file)
if config_json["personalization"]["permission"]:
personalization = True
open_ai_key = config_json["personalization"]["api_key"]
else:
request_personalization()
if config_json["personalization"]["permission"]:
personalization = True
open_ai_key = config_json["personalization"]["api_key"]

print (personalization)
print (open_ai_key)


commands = []
if args.history:
commands = get_history_commands(HISTORY_FILE)


if (personalization):
personalized_history = personalize(user_input, commands, open_ai_key, False)
print(personalized_history)


else:
with Halo(text=f"{GORILLA_EMOJI}Loading", spinner="dots"):
try:
data_json = {
"user_id": user_id,
#"synthesized_history": personalized_history,
"user_input": user_input,
"interaction_id": interaction_id,
"system_info": system_info
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

setup(
name="gorilla-cli",
version="0.0.11",
version="0.0.12",
url="https://github.com/gorilla-llm/gorilla-cli",
author="Shishir Patil, Tianjun Zhang",
author_email="sgp@berkeley.edu, tianjunz@berkeley.edu",
Expand Down
77 changes: 77 additions & 0 deletions utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import os
from presidio_analyzer import AnalyzerEngine, PatternRecognizer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you also need to include this in the requirements presidio_analyzer

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where would I access the file for this?

from presidio_anonymizer import AnonymizerEngine
from presidio_anonymizer.entities import OperatorConfig
import json
from pprint import pprint
from openai import OpenAI

def get_bash_history():
history_file = os.path.expanduser("~/.bash_history")
prev_operations = ""
try:
with open(history_file, "r") as file:
history = file.readlines()
except FileNotFoundError:
return "No bash history was found."
return history[:-10]


def anonymize_bash_history(operations):
analyzer = AnalyzerEngine()
analyzer_results = analyzer.analyze(text=operations, language="en")
anonymizer = AnonymizerEngine()
anonymized_results = anonymizer.anonymize(
text=operations, analyzer_results=analyzer_results
)
return anonymized_results.text


def remove_duplicates(operations: list[str]):
return list(set(operations))


def stringify_bash_history(operations: list[str]):
return "\n".join(operations)


def synthesize_bash_history(client, desired_operation, gorila_history, history):
SYSTEM_PROMPT = """
You are an assistant for a developer who wants to find the right API call for a specific task.
The developer has bash history that contains the command they used to perform a task.
Synthesize their bash history to provide the API call prediction model with extra context about the task.
For reference, the API call prediction model, called Gorilla, is trained on a large dataset of API calls and their associated tasks.
You may see the developer's previous operations with the API calling tool in their bash history.
Use the previous bash history as well as their query to provide the model with a short paragraph of possible relevant context.
There is a chance that their query has nothing to do with the bash history, so in that case, return 'No relevant context found'.
"""
USER_PROMPT = f"""
The user's bash history is:
{history}

The user's previous operations with the API calling tool are:
{gorila_history}

The query of the user is:
{desired_operation}

Use this information to provide the model with a short paragraph of possible relevant context.
"""

response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": USER_PROMPT},
],
)
return response.choices[0].message.content


def personalize(query, gorilla_history, open_ai_key, pi_removal=True):
history = stringify_bash_history(remove_duplicates(get_bash_history()))
client = OpenAI(api_key=open_ai_key)
if pi_removal:
history = anonymize_bash_history(history)
summary = synthesize_bash_history(client, query, gorilla_history, history)
return summary