From 71030c66b2b23aa11a2edb2a833aeadfc8950227 Mon Sep 17 00:00:00 2001 From: Yingbei Date: Fri, 19 Jul 2024 14:47:46 -0700 Subject: [PATCH 1/4] first version of email assist demo --- .gitignore | 4 + demo/credentials_example.json | 1 + demo/email_assist.py | 64 +++++++++++++ demo/email_operations.py | 170 ++++++++++++++++++++++++++++++++++ demo/email_tools.py | 98 ++++++++++++++++++++ 5 files changed, 337 insertions(+) create mode 100644 demo/credentials_example.json create mode 100644 demo/email_assist.py create mode 100644 demo/email_operations.py create mode 100644 demo/email_tools.py diff --git a/.gitignore b/.gitignore index ceb23c3..bc3531f 100644 --- a/.gitignore +++ b/.gitignore @@ -239,3 +239,7 @@ zipalign* *.sln *.sw? +# demo files +demo/credentials.json +demo/token.json + diff --git a/demo/credentials_example.json b/demo/credentials_example.json new file mode 100644 index 0000000..a9132d1 --- /dev/null +++ b/demo/credentials_example.json @@ -0,0 +1 @@ +{"installed":{"client_id":"xxx.apps.googleusercontent.com","project_id":"xxx","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"xxx","redirect_uris":["http://localhost"]}} \ No newline at end of file diff --git a/demo/email_assist.py b/demo/email_assist.py new file mode 100644 index 0000000..0f1a278 --- /dev/null +++ b/demo/email_assist.py @@ -0,0 +1,64 @@ +import openai +openai.api_key = "123" +openai.base_url = "http://localhost:1234/v1/" + +model = "gpt4o" + +from email_tools import functions, tool_call_mapping +import json + +msgs = [] +system_prompt = "You are a helpful assistant." +user_query = "look at my new emails today one by one. For each of them, if the content looks not important, such as commercials or utility bill, mark it as read." + +def run_chat(user_query, msgs = []): + if not msgs or len(msgs) == 0: + msgs = [{"role": "system", "content":system_prompt} ,{"role": "user", "content": user_query}] + else: + msgs.append({"role": "user", "content": user_query}) + try: + completion = openai.chat.completions.create( + model=model, + temperature=0.1, + messages=msgs, + tools=functions, + tool_choice="auto", + stream=False, + ) + res = completion.choices[0] + return res, msgs + except Exception as e: + print(f"Error : {e}") + +res, msgs = run_chat(user_query=user_query) +while res.message.tool_calls: + tool_calls = [] + func_output =[] + for tool_call in res.message.tool_calls: + + func_name,func_args = tool_call.function.name, tool_call.function.arguments + print(f"\n=====calling function : {func_name}, with args: {func_args}") + func_args = json.loads(func_args) + tool_calls.append( { + "id": tool_call.id, + "function": {"name": func_name, + "arguments": func_args}, + "type": "function", + }) + func_to_run = tool_call_mapping[func_name] + observation = func_to_run(**func_args) + # print(f"Observation: {observation}") + func_output.append([tool_call.id, func_name, str(observation)]) + msgs.append({"role": "assistant", "tool_calls": tool_calls}) + for id,func_name, o in func_output: + msgs.append({ + "role": "tool", + "name": func_name, + "content": o, + "tool_call_id": id + }) + res, msgs = run_chat(user_query=user_query, msgs=msgs) +final_res = res.message.content +print(final_res) + + \ No newline at end of file diff --git a/demo/email_operations.py b/demo/email_operations.py new file mode 100644 index 0000000..47f745e --- /dev/null +++ b/demo/email_operations.py @@ -0,0 +1,170 @@ +''' +https://developers.google.com/gmail/api/quickstart/python +''' + +import os.path +import base64 +from email.message import EmailMessage +from google.auth.transport.requests import Request +from google.oauth2.credentials import Credentials +from google_auth_oauthlib.flow import InstalledAppFlow +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + +# If modifying these scopes, delete the file token.json. +SCOPES = ["https://mail.google.com/"] +# SCOPES = ["https://www.googleapis.com/auth/gmail.compose", "https://www.googleapis.com/auth/gmail.readonly"] + +def auth(): + """Shows basic usage of the Gmail API. + Lists the user's Gmail labels. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w") as token: + token.write(creds.to_json()) + return creds + +def decode_base64(data): + decoded_bytes = base64.urlsafe_b64decode(data) + decoded_str = decoded_bytes.decode('utf-8') + return decoded_str + + +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + +def gmail_send_message(): + """Create and insert a draft email. + Print the returned draft's message and id. + Returns: Draft object, including draft id and message meta data. + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + + try: + # create gmail api client + service = build("gmail", "v1", credentials=auth()) + + message = EmailMessage() + + message.set_content("This is automated draft mail") + + message["To"] = ["yingbei@acorn.io", "tybalex@gmail.com"] + message["From"] = "tybalex@gmail.com" + message["Subject"] = "Automated draft" + + # encoded message + encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode() + + create_message = {"raw": encoded_message} + # pylint: disable=E1101 + send_message = ( + service.users() + .messages() + .send(userId="me", body=create_message) + .execute() + ) + print(f'Message Id: {send_message["id"]}') + except HttpError as error: + print(f"An error occurred: {error}") + send_message = None + return send_message + + +def mark_as_read( email_id): + service = build("gmail", "v1", credentials=auth()) + service.users().messages().modify(userId='me', id=email_id, body={'removeLabelIds': ['UNREAD']}).execute() + + +def read_message(email_id): + service = build("gmail", "v1", credentials=auth()) + msg = service.users().messages().get(userId='me', id=email_id).execute() + + # Extract the parts + email_data = msg + content_text = "" + if "parts" not in email_data['payload']: + return content_text + else: + parts = email_data['payload']['parts'] + decoded_parts = {} + + for part in parts: + mime_type = part['mimeType'] + encoded_data = part['body']['data'] + decoded_content = decode_base64(encoded_data) + decoded_parts[mime_type] = decoded_content + + # Extract necessary information + content_text = decoded_parts.get('text/plain', 'No Plain Text Content') + return content_text + + + + +def list_messages(n=5, date = None): + try: + # create gmail api client + service = build("gmail", "v1", credentials=auth()) + results = ( + service.users() + .messages() + .list(userId="me", labelIds=["UNREAD"]) + .execute() + ) + messages = results.get('messages',[]) + res = [] + if not messages: + print('No new messages.') + else: + for i, message in enumerate(messages): + if i >= n: + break + msg = service.users().messages().get(userId='me', id=message['id']).execute() + + # Extract the parts + email_data = msg + headers = email_data['payload']['headers'] + header_dict = {header['name']: header['value'] for header in headers} + + # Print the extracted information + title = header_dict.get('Subject', 'No Subject') + sender = header_dict.get('From', 'No Sender') + receiver = header_dict.get('To', 'No Receiver') + date = header_dict.get("Date", "No Date Received") + res.append({ + "id" : message['id'], + "title": title, + "sender": sender, + "receiver": receiver, + "date": date, + # "content": content_text, + }) + # print(f"Content (HTML):\n{content_html}") + return res + + except HttpError as error: + print(f"An error occurred: {error}") + +if __name__ == "__main__": +# gmail_send_message() + res = list_messages() + print(res) \ No newline at end of file diff --git a/demo/email_tools.py b/demo/email_tools.py new file mode 100644 index 0000000..d42aff9 --- /dev/null +++ b/demo/email_tools.py @@ -0,0 +1,98 @@ +from email_operations import mark_as_read, list_messages, read_message + +functions = [ + { + "type": "function", + "function": { + "name": "get_today_date", + "description": "get today's date and time", + "parameters": { + "type": "object", + "properties": { + # "directory": { + # "type": "string", + # "description": "the directory to list files from" + # } + }, + "required": [ + # "directory" + ] + } + } + }, + { + "type": "function", + "function": { + "name": "list_unread_emails", + "description": "List all unread emails in the mailbox", + "parameters": { + "type": "object", + "properties": { + "n": { + "type": "integer", + "description": "the number of emails to return, default = 5" + }, + "date": { + "type": "string", + "description": "list unread email for a specific date, in yyyy-mm-dd format, default is None. Useful when user want emails for a certain day" + }, + + }, + "required": [ + # "directory" + ] + } + } + }, + { + "type": "function", + "function": { + "name": "mark_email_as_read", + "description": "change the status of an email to `read`", + "parameters": { + "type": "object", + "properties": { + "email_id": { + "type": "string", + "description": "the id of the unread email to be marked as read." + } + }, + "required": [ + "email_id" + ] + } + } + }, + { + "type": "function", + "function": { + "name": "read_email", + "description": "read an email and get the content", + "parameters": { + "type": "object", + "properties": { + "email_id": { + "type": "string", + "description": "the id of the email to read content." + } + }, + "required": [ + "email_id" + ] + } + } + }, +] + +def get_date(): + return "2024-07-18" + + + + +tool_call_mapping = { + "get_today_date": get_date, + "list_unread_emails": list_messages, + "mark_email_as_read": mark_as_read, + "read_email": read_message +} \ No newline at end of file From 4c0c023b9006a477ee8824d26d0d2ce7a86fb7b9 Mon Sep 17 00:00:00 2001 From: Yingbei Date: Mon, 22 Jul 2024 15:22:47 -0700 Subject: [PATCH 2/4] A working demo example, also about to add readme for more details to run --- demo/README.md | 6 ++ demo/email_assist.py | 64 ---------------------- demo/email_operations.py | 78 +++++++++++++------------- demo/email_tools.py | 106 +++++++++++++++++++++++++----------- demo/main_email_assist.py | 10 ++++ demo/run_chat_completion.py | 25 +++++++++ 6 files changed, 154 insertions(+), 135 deletions(-) create mode 100644 demo/README.md delete mode 100644 demo/email_assist.py create mode 100644 demo/main_email_assist.py create mode 100644 demo/run_chat_completion.py diff --git a/demo/README.md b/demo/README.md new file mode 100644 index 0000000..a78b880 --- /dev/null +++ b/demo/README.md @@ -0,0 +1,6 @@ +## Rubra model demo + +This demo will walk you through an example that how you can connect rubra tool-call model to your gmail mailbox, and let the ai assistant helps you take care of your emails. + +### Get Started + diff --git a/demo/email_assist.py b/demo/email_assist.py deleted file mode 100644 index 0f1a278..0000000 --- a/demo/email_assist.py +++ /dev/null @@ -1,64 +0,0 @@ -import openai -openai.api_key = "123" -openai.base_url = "http://localhost:1234/v1/" - -model = "gpt4o" - -from email_tools import functions, tool_call_mapping -import json - -msgs = [] -system_prompt = "You are a helpful assistant." -user_query = "look at my new emails today one by one. For each of them, if the content looks not important, such as commercials or utility bill, mark it as read." - -def run_chat(user_query, msgs = []): - if not msgs or len(msgs) == 0: - msgs = [{"role": "system", "content":system_prompt} ,{"role": "user", "content": user_query}] - else: - msgs.append({"role": "user", "content": user_query}) - try: - completion = openai.chat.completions.create( - model=model, - temperature=0.1, - messages=msgs, - tools=functions, - tool_choice="auto", - stream=False, - ) - res = completion.choices[0] - return res, msgs - except Exception as e: - print(f"Error : {e}") - -res, msgs = run_chat(user_query=user_query) -while res.message.tool_calls: - tool_calls = [] - func_output =[] - for tool_call in res.message.tool_calls: - - func_name,func_args = tool_call.function.name, tool_call.function.arguments - print(f"\n=====calling function : {func_name}, with args: {func_args}") - func_args = json.loads(func_args) - tool_calls.append( { - "id": tool_call.id, - "function": {"name": func_name, - "arguments": func_args}, - "type": "function", - }) - func_to_run = tool_call_mapping[func_name] - observation = func_to_run(**func_args) - # print(f"Observation: {observation}") - func_output.append([tool_call.id, func_name, str(observation)]) - msgs.append({"role": "assistant", "tool_calls": tool_calls}) - for id,func_name, o in func_output: - msgs.append({ - "role": "tool", - "name": func_name, - "content": o, - "tool_call_id": id - }) - res, msgs = run_chat(user_query=user_query, msgs=msgs) -final_res = res.message.content -print(final_res) - - \ No newline at end of file diff --git a/demo/email_operations.py b/demo/email_operations.py index 47f745e..36f1b76 100644 --- a/demo/email_operations.py +++ b/demo/email_operations.py @@ -94,30 +94,49 @@ def mark_as_read( email_id): service.users().messages().modify(userId='me', id=email_id, body={'removeLabelIds': ['UNREAD']}).execute() + def read_message(email_id): service = build("gmail", "v1", credentials=auth()) msg = service.users().messages().get(userId='me', id=email_id).execute() - # Extract the parts + # Extract the parts email_data = msg + headers = email_data['payload']['headers'] + header_dict = {header['name']: header['value'] for header in headers} + + # Print the extracted information + title = header_dict.get('Subject', 'No Subject') + sender = header_dict.get('From', 'No Sender') + receiver = header_dict.get('To', 'No Receiver') + date = header_dict.get("Date", "No Date Received") + content_text = "" - if "parts" not in email_data['payload']: - return content_text - else: - parts = email_data['payload']['parts'] - decoded_parts = {} - - for part in parts: - mime_type = part['mimeType'] - encoded_data = part['body']['data'] - decoded_content = decode_base64(encoded_data) - decoded_parts[mime_type] = decoded_content - - # Extract necessary information - content_text = decoded_parts.get('text/plain', 'No Plain Text Content') - return content_text + try: + if "parts" not in email_data['payload']: + parts = [] + else: + parts = email_data['payload']['parts'] + decoded_parts = {} + + for part in parts: + mime_type = part['mimeType'] + encoded_data = part['body']['data'] + decoded_content = decode_base64(encoded_data) + decoded_parts[mime_type] = decoded_content + + # Extract necessary information + content_text = decoded_parts.get('text/plain', 'No Plain Text Content') + except Exception as e: + print(e) - + return { + "id" : email_id, + "title": title, + "sender": sender, + "receiver": receiver, + "date": date, + "content_text": content_text + } def list_messages(n=5, date = None): @@ -138,28 +157,9 @@ def list_messages(n=5, date = None): for i, message in enumerate(messages): if i >= n: break - msg = service.users().messages().get(userId='me', id=message['id']).execute() - - # Extract the parts - email_data = msg - headers = email_data['payload']['headers'] - header_dict = {header['name']: header['value'] for header in headers} - - # Print the extracted information - title = header_dict.get('Subject', 'No Subject') - sender = header_dict.get('From', 'No Sender') - receiver = header_dict.get('To', 'No Receiver') - date = header_dict.get("Date", "No Date Received") - res.append({ - "id" : message['id'], - "title": title, - "sender": sender, - "receiver": receiver, - "date": date, - # "content": content_text, - }) - # print(f"Content (HTML):\n{content_html}") - return res + res.append(message["id"]) + return res + except HttpError as error: print(f"An error occurred: {error}") diff --git a/demo/email_tools.py b/demo/email_tools.py index d42aff9..0224994 100644 --- a/demo/email_tools.py +++ b/demo/email_tools.py @@ -1,25 +1,45 @@ from email_operations import mark_as_read, list_messages, read_message +from run_chat_completion import run_chat +import json -functions = [ - { - "type": "function", - "function": { - "name": "get_today_date", - "description": "get today's date and time", - "parameters": { - "type": "object", - "properties": { - # "directory": { - # "type": "string", - # "description": "the directory to list files from" - # } - }, - "required": [ - # "directory" - ] - } - } - }, +default_system_prompt = "You are a helpful assistant." + +def run_agent(user_query, functions, system_prompt=default_system_prompt): + print(f"User query: {user_query}") + res, msgs = run_chat(user_query=user_query, functions=functions, system_prompt=system_prompt) + while res.message.tool_calls: + tool_calls = [] + func_output =[] + for tool_call in res.message.tool_calls: + + func_name,func_args = tool_call.function.name, tool_call.function.arguments + print(f"\n=====calling function : {func_name}, with args: {func_args}") + tool_calls.append( { + "id": tool_call.id, + "function": {"name": func_name, + "arguments": func_args}, + "type": "function", + }) + func_args = json.loads(func_args) + func_to_run = tool_call_mapping[func_name] + observation = func_to_run(**func_args) + # print(f"Observation: {observation}") + func_output.append([tool_call.id, func_name, str(observation)]) + msgs.append({"role": "assistant", "tool_calls": tool_calls}) + for id,func_name, o in func_output: + msgs.append({ + "role": "tool", + "name": func_name, + "content": o, + "tool_call_id": id + }) + res, msgs = run_chat(user_query=user_query,functions=functions, msgs=msgs) + final_res = res.message.content + print(final_res) + return final_res + + +main_functions = [ { "type": "function", "function": { @@ -39,7 +59,7 @@ }, "required": [ - # "directory" + ] } } @@ -47,7 +67,7 @@ { "type": "function", "function": { - "name": "mark_email_as_read", + "name": "change_email_to_read", "description": "change the status of an email to `read`", "parameters": { "type": "object", @@ -66,14 +86,14 @@ { "type": "function", "function": { - "name": "read_email", - "description": "read an email and get the content", + "name": "label_email", + "description": "read an email and label it with one of the three label: work, daily, important", "parameters": { "type": "object", "properties": { "email_id": { "type": "string", - "description": "the id of the email to read content." + "description": "the id of the email to process." } }, "required": [ @@ -84,15 +104,37 @@ }, ] -def get_date(): - return "2024-07-18" - +def label_message(email_id) -> str: + """This is a rule based example to label emails. It's also possible to use LLM's help to do so. + Args: + email_id (_type_): _description_ + Return: + one of the three label: [work, daily, important] + """ + msg_detail = read_message(email_id) + print(msg_detail["title"]) + print(msg_detail["date"]) + print(msg_detail["sender"]) + print(msg_detail["receiver"]) + + # Now do some rule based stuff or use LLM or some model to label the email + label = "daily" + if "@acorn.io" in msg_detail["sender"]: + label = "work" + # some arbitrary keyword rule based stuff + elif "REMINDER" in msg_detail["title"] or "important" in msg_detail["content_text"]: + label = "important" + return f"Label: {label}" + tool_call_mapping = { - "get_today_date": get_date, "list_unread_emails": list_messages, - "mark_email_as_read": mark_as_read, - "read_email": read_message -} \ No newline at end of file + "change_email_to_read": mark_as_read, + "label_email": label_message, +} + + + + diff --git a/demo/main_email_assist.py b/demo/main_email_assist.py new file mode 100644 index 0000000..1ace7e7 --- /dev/null +++ b/demo/main_email_assist.py @@ -0,0 +1,10 @@ + +from email_tools import run_agent, main_functions + +msgs = [] +user_query = "Process my last 5 emails, get the label for each of them, then change the emails with a `daily` label to `read` status." + +run_agent(user_query, main_functions) + + + \ No newline at end of file diff --git a/demo/run_chat_completion.py b/demo/run_chat_completion.py new file mode 100644 index 0000000..fd4d0fb --- /dev/null +++ b/demo/run_chat_completion.py @@ -0,0 +1,25 @@ +import openai +openai.api_key = "sk-" +openai.base_url = "http://localhost:1234/v1/" + +model = "gpt-4o" + + +def run_chat(user_query, system_prompt = "", functions=[], msgs = []): + if not msgs or len(msgs) == 0: + msgs = [{"role": "system", "content":system_prompt} ,{"role": "user", "content": user_query}] + else: + msgs.append({"role": "user", "content": user_query}) + try: + completion = openai.chat.completions.create( + model=model, + temperature=0.1, + messages=msgs, + tools=functions, + tool_choice="auto", + stream=False, + ) + res = completion.choices[0] + return res, msgs + except Exception as e: + print(f"Error : {e}") \ No newline at end of file From b8e9d3550117c8713754ebb73d4671e7f04e0104 Mon Sep 17 00:00:00 2001 From: Yingbei Date: Mon, 22 Jul 2024 16:26:15 -0700 Subject: [PATCH 3/4] finallized example --- demo/email_operations.py | 2 ++ demo/email_tools.py | 3 ++- demo/main_email_assist.py | 5 +++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/demo/email_operations.py b/demo/email_operations.py index 36f1b76..e30b4e5 100644 --- a/demo/email_operations.py +++ b/demo/email_operations.py @@ -158,6 +158,8 @@ def list_messages(n=5, date = None): if i >= n: break res.append(message["id"]) + + print(res) return res diff --git a/demo/email_tools.py b/demo/email_tools.py index 0224994..93463e7 100644 --- a/demo/email_tools.py +++ b/demo/email_tools.py @@ -35,7 +35,6 @@ def run_agent(user_query, functions, system_prompt=default_system_prompt): }) res, msgs = run_chat(user_query=user_query,functions=functions, msgs=msgs) final_res = res.message.content - print(final_res) return final_res @@ -126,6 +125,8 @@ def label_message(email_id) -> str: # some arbitrary keyword rule based stuff elif "REMINDER" in msg_detail["title"] or "important" in msg_detail["content_text"]: label = "important" + + print(label) return f"Label: {label}" diff --git a/demo/main_email_assist.py b/demo/main_email_assist.py index 1ace7e7..aefdefa 100644 --- a/demo/main_email_assist.py +++ b/demo/main_email_assist.py @@ -2,9 +2,10 @@ from email_tools import run_agent, main_functions msgs = [] -user_query = "Process my last 5 emails, get the label for each of them, then change the emails with a `daily` label to `read` status." +user_query = "Process my last 5 emails. get the label for all of them, then change the emails with a `daily` label to `read` status." -run_agent(user_query, main_functions) +final_res = run_agent(user_query, main_functions) +print(f"Final AI Response: {final_res}") \ No newline at end of file From 128657121e55a304f7fd432b866935837a5af630 Mon Sep 17 00:00:00 2001 From: Yingbei Date: Mon, 22 Jul 2024 19:25:23 -0700 Subject: [PATCH 4/4] Rename dir, update Main readme, update gitignore --- .gitignore | 5 ++- README.md | 2 + demo/README.md | 6 --- demo/email_assistant/README.md | 45 +++++++++++++++++++ .../credentials_example.json | 0 .../{ => email_assistant}/email_operations.py | 0 demo/{ => email_assistant}/email_tools.py | 0 .../main_email_assistant.py} | 0 demo/email_assistant/requirements.txt | 4 ++ .../run_chat_completion.py | 0 10 files changed, 54 insertions(+), 8 deletions(-) delete mode 100644 demo/README.md create mode 100644 demo/email_assistant/README.md rename demo/{ => email_assistant}/credentials_example.json (100%) rename demo/{ => email_assistant}/email_operations.py (100%) rename demo/{ => email_assistant}/email_tools.py (100%) rename demo/{main_email_assist.py => email_assistant/main_email_assistant.py} (100%) create mode 100644 demo/email_assistant/requirements.txt rename demo/{ => email_assistant}/run_chat_completion.py (100%) diff --git a/.gitignore b/.gitignore index bc3531f..ded2946 100644 --- a/.gitignore +++ b/.gitignore @@ -240,6 +240,7 @@ zipalign* *.sw? # demo files -demo/credentials.json -demo/token.json +demo/email_assistant/credentials.json +demo/email_assistant/token.json +yarn.lock \ No newline at end of file diff --git a/README.md b/README.md index 2af096a..2992195 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,8 @@ All models are enhanced from the top open-source LLMs with further post-training Try out the models immediately without downloading anything in Our [Huggingface Spaces]([https://huggingface.co/spaces/sanjay920/rubra-v0.1-dev](https://huggingface.co/spaces/sanjay920/rubra-v0.1-function-calling))! It's free and requires no login. +For more examples, please check out the `demo` directory. + ## Run Rubra Models Locally Check out our [documentation](https://docs.rubra.ai/category/serving--inferencing) to learn how to run Rubra models locally. diff --git a/demo/README.md b/demo/README.md deleted file mode 100644 index a78b880..0000000 --- a/demo/README.md +++ /dev/null @@ -1,6 +0,0 @@ -## Rubra model demo - -This demo will walk you through an example that how you can connect rubra tool-call model to your gmail mailbox, and let the ai assistant helps you take care of your emails. - -### Get Started - diff --git a/demo/email_assistant/README.md b/demo/email_assistant/README.md new file mode 100644 index 0000000..3dc2ab9 --- /dev/null +++ b/demo/email_assistant/README.md @@ -0,0 +1,45 @@ +## Rubra model demo + +This demo will walk you through an example that how you can connect rubra tool-call model to your gmail mailbox, and let the ai assistant helps you take care of your emails. + +*In this demo, the assistant is granted privileges only to read your emails and change the status of an email from unread to read.* + +### Prerequisites: +- Python 3.10.7 or greater, with the pip package management tool +- A Google Cloud project. +- Your Google account with Gmail enabled. + +### Get Started + +**1.Start a Rubra Model server:** +Use either [tools.cpp](https://github.com/rubra-ai/tools.cpp?tab=readme-ov-file#toolscpp-quickstart) or [vLLM](https://github.com/rubra-ai/vllm?tab=readme-ov-file#rubra-vllm-quickstart) to serve a Rubra model. + +**2.Enable Gmail API and setup authentication:** +A few things to config to allow the AI assistant to connect to your gmail emails thru Gmail API: +- In the Google Cloud console, [enable the Gmail API](https://console.cloud.google.com/flows/enableapi?apiid=gmail.googleapis.com). +- [Configure the OAuth consent screen](https://developers.google.com/gmail/api/quickstart/python#configure_the_oauth_consent_screen): For User type select Internal, if you can't then simply select external. +- [Authorize credentials for a desktop application](https://developers.google.com/gmail/api/quickstart/python#authorize_credentials_for_a_desktop_application): Don't forget to download `credentials.json` to the `demo` dir or where you'd like to run the code. + +Reference: https://developers.google.com/gmail/api/quickstart/python#set_up_your_environment + +**3.Pip install and Run the python script:** +```python +pip install -r requirements.txt +``` +and then: +```python +python main_email_assistant.py +``` + +The user prompt in this script: +``` +Process my last 5 emails. get the label for all of them, then change the emails with a `daily` label to `read` status. +``` +If everything goes well, the AI assistant will look at the latest 5 emails and mark some of them as `read`. + +### What's next? +In the demo, the assistant is granted privileges only to: +- list and read emails +- change the status of emails from `unread` to `read`. + +You can definitely enhance its capabilities by introducing more tools/functions, such as moving emails to different folders/inboxes, drafting and sending emails, etc. \ No newline at end of file diff --git a/demo/credentials_example.json b/demo/email_assistant/credentials_example.json similarity index 100% rename from demo/credentials_example.json rename to demo/email_assistant/credentials_example.json diff --git a/demo/email_operations.py b/demo/email_assistant/email_operations.py similarity index 100% rename from demo/email_operations.py rename to demo/email_assistant/email_operations.py diff --git a/demo/email_tools.py b/demo/email_assistant/email_tools.py similarity index 100% rename from demo/email_tools.py rename to demo/email_assistant/email_tools.py diff --git a/demo/main_email_assist.py b/demo/email_assistant/main_email_assistant.py similarity index 100% rename from demo/main_email_assist.py rename to demo/email_assistant/main_email_assistant.py diff --git a/demo/email_assistant/requirements.txt b/demo/email_assistant/requirements.txt new file mode 100644 index 0000000..254454b --- /dev/null +++ b/demo/email_assistant/requirements.txt @@ -0,0 +1,4 @@ +openai +google-api-python-client +google-auth-httplib2 +google-auth-oauthlib \ No newline at end of file diff --git a/demo/run_chat_completion.py b/demo/email_assistant/run_chat_completion.py similarity index 100% rename from demo/run_chat_completion.py rename to demo/email_assistant/run_chat_completion.py