From 6e3f2d3ce2850020890b517f168f163aa5b1622f Mon Sep 17 00:00:00 2001 From: Etancher <30360093+jahanvir@users.noreply.github.com> Date: Sat, 2 Sep 2023 17:38:06 +0530 Subject: [PATCH 1/9] Added support for google palm API --- main.py | 8 +++----- pyproject.toml | 1 + textbase/models.py | 37 ++++++++++++++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/main.py b/main.py index 65a75016..73cb37bf 100644 --- a/main.py +++ b/main.py @@ -1,12 +1,12 @@ import os from textbase import bot, Message -from textbase.models import OpenAI +from textbase.models import PalmAI from typing import List # Load your OpenAI API key # OpenAI.api_key = "" # or from environment variable: -OpenAI.api_key = os.getenv("OPENAI_API_KEY") +PalmAI.api_key = os.getenv("PALM_API_KEY") # Prompt for GPT-3.5 Turbo SYSTEM_PROMPT = """You are chatting with an AI. There are no specific prefixes for responses, so you can ask or talk about anything you like. @@ -17,10 +17,8 @@ def on_message(message_history: List[Message], state: dict = None): # Generate GPT-3.5 Turbo response - bot_response = OpenAI.generate( - system_prompt=SYSTEM_PROMPT, + bot_response = PalmAI.generate( message_history=message_history, # Assuming history is the list of user messages - model="gpt-3.5-turbo", ) response = { diff --git a/pyproject.toml b/pyproject.toml index fd2dd53a..8738793e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,7 @@ tabulate = "^0.9.0" functions-framework = "^3.4.0" yaspin = "^3.0.0" pydantic = "^2.3.0" +google-generativeai = "^0.1.0" [build-system] requires = ["poetry-core"] diff --git a/textbase/models.py b/textbase/models.py index 814ed533..96c9d36e 100644 --- a/textbase/models.py +++ b/textbase/models.py @@ -1,5 +1,6 @@ import json import openai +import google.generativeai as palm import requests import time import typing @@ -26,6 +27,14 @@ def extract_content_values(message: Message): if content ] +# Returns user content if it's non empty. +def extract_user_content_values(message:Message): + return [ + content["content"] + for content in get_contents(message,"STRING") + if content and content["role"] == "user" + ] + class OpenAI: api_key = None @@ -143,4 +152,30 @@ def generate( data = json.loads(response.text) # parse the JSON data into a dictionary message = data['message'] - return message \ No newline at end of file + return message + +class PalmAI: + api_key = None + + @classmethod + def generate( + cls, + message_history: list[Message], + ): + + assert cls.api_key is not None, "Palm API key is not set." + palm.configure(api_key=cls.api_key) + + filtered_messages = [] + + for message in message_history: + #list of all the contents inside a single message + contents = extract_user_content_values(message) + if contents: + filtered_messages.extend(contents) + + #send request to Google Palm chat API + response = palm.chat(messages=filtered_messages) + + print(response) + return response.last \ No newline at end of file From 3072aea13e5d1df8ea1ea5cc49fe076a383b7167 Mon Sep 17 00:00:00 2001 From: Etancher <30360093+jahanvir@users.noreply.github.com> Date: Sat, 2 Sep 2023 17:38:37 +0530 Subject: [PATCH 2/9] added palm API key in .env.example --- .env.example | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 54430edb..5580e660 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,3 @@ OPENAI_API_KEY= -HUGGINGFACEHUB_API_TOKEN= \ No newline at end of file +HUGGINGFACEHUB_API_TOKEN= +PALM_API_KEY= \ No newline at end of file From 9797c8270742fb8839c0cc76b79486a200030bfb Mon Sep 17 00:00:00 2001 From: Etancher <30360093+jahanvir@users.noreply.github.com> Date: Sat, 2 Sep 2023 17:41:29 +0530 Subject: [PATCH 3/9] added Google Palm AI example --- examples/palmAI-bot.py | 39 +++++++++++++++++++++++++++++++++++++++ main.py | 10 ++-------- 2 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 examples/palmAI-bot.py diff --git a/examples/palmAI-bot.py b/examples/palmAI-bot.py new file mode 100644 index 00000000..bfe8a0d5 --- /dev/null +++ b/examples/palmAI-bot.py @@ -0,0 +1,39 @@ +import os +from textbase import bot, Message +from textbase.models import PalmAI +from typing import List + +# Load your PALM API key +# PALMAI.api_key = "" +# or from environment variable: +PalmAI.api_key = os.getenv("PALM_API_KEY") + + +@bot() +def on_message(message_history: List[Message], state: dict = None): + + bot_response = PalmAI.generate( + message_history=message_history, # Assuming history is the list of user messages + ) + + response = { + "data": { + "messages": [ + { + "data_type": "STRING", + "value": bot_response + } + ], + "state": state + }, + "errors": [ + { + "message": "" + } + ] + } + + return { + "status_code": 200, + "response": response + } diff --git a/main.py b/main.py index 73cb37bf..a7f74890 100644 --- a/main.py +++ b/main.py @@ -3,20 +3,14 @@ from textbase.models import PalmAI from typing import List -# Load your OpenAI API key -# OpenAI.api_key = "" +# Load your PALM API key +# PalmAI.api_key = "" # or from environment variable: PalmAI.api_key = os.getenv("PALM_API_KEY") -# Prompt for GPT-3.5 Turbo -SYSTEM_PROMPT = """You are chatting with an AI. There are no specific prefixes for responses, so you can ask or talk about anything you like. -You will respond in a natural, conversational manner. Feel free to start the conversation with any question or topic, and let's have a pleasant chat! -""" - @bot() def on_message(message_history: List[Message], state: dict = None): - # Generate GPT-3.5 Turbo response bot_response = PalmAI.generate( message_history=message_history, # Assuming history is the list of user messages ) From 6ddaec7af0ccbf0b465d063e19e420908dd1d26d Mon Sep 17 00:00:00 2001 From: Etancher <30360093+jahanvir@users.noreply.github.com> Date: Sun, 3 Sep 2023 16:11:16 +0530 Subject: [PATCH 4/9] merged changes --- examples/{palmAI-bot.py => palmai-bot/main.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{palmAI-bot.py => palmai-bot/main.py} (100%) diff --git a/examples/palmAI-bot.py b/examples/palmai-bot/main.py similarity index 100% rename from examples/palmAI-bot.py rename to examples/palmai-bot/main.py From a88e18eb3a690ec879c32e8b7f5470ef02761081 Mon Sep 17 00:00:00 2001 From: Etancher <30360093+jahanvir@users.noreply.github.com> Date: Sun, 3 Sep 2023 16:58:27 +0530 Subject: [PATCH 5/9] added script to zip the requirement and main file for deploy. --- zip.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 zip.py diff --git a/zip.py b/zip.py new file mode 100644 index 00000000..ff0079c6 --- /dev/null +++ b/zip.py @@ -0,0 +1,20 @@ +import zipfile +import os + +# Define the names of the files to be zipped +files_to_zip = ['requirements.txt', 'main.py'] + +# Define the name of the output zip file as "deploy.zip" +output_zip_filename = 'deploy.zip' + +# Create a ZipFile object in write mode +with zipfile.ZipFile(output_zip_filename, 'w', zipfile.ZIP_DEFLATED) as zipf: + for file_to_zip in files_to_zip: + # Check if the file exists in the current directory + if os.path.exists(file_to_zip): + # Add the file to the zip archive + zipf.write(file_to_zip, os.path.basename(file_to_zip)) + else: + print(f"Warning: {file_to_zip} not found in the current directory.") + +print(f"Files {', '.join(files_to_zip)} have been zipped to {output_zip_filename}") From 5cb2eb4d5465d70c96b93b6e5d42c9c6b61d8d61 Mon Sep 17 00:00:00 2001 From: Etancher <30360093+jahanvir@users.noreply.github.com> Date: Sun, 3 Sep 2023 17:05:15 +0530 Subject: [PATCH 6/9] Delete zip.py --- zip.py | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 zip.py diff --git a/zip.py b/zip.py deleted file mode 100644 index ff0079c6..00000000 --- a/zip.py +++ /dev/null @@ -1,20 +0,0 @@ -import zipfile -import os - -# Define the names of the files to be zipped -files_to_zip = ['requirements.txt', 'main.py'] - -# Define the name of the output zip file as "deploy.zip" -output_zip_filename = 'deploy.zip' - -# Create a ZipFile object in write mode -with zipfile.ZipFile(output_zip_filename, 'w', zipfile.ZIP_DEFLATED) as zipf: - for file_to_zip in files_to_zip: - # Check if the file exists in the current directory - if os.path.exists(file_to_zip): - # Add the file to the zip archive - zipf.write(file_to_zip, os.path.basename(file_to_zip)) - else: - print(f"Warning: {file_to_zip} not found in the current directory.") - -print(f"Files {', '.join(files_to_zip)} have been zipped to {output_zip_filename}") From 28316fa983782a245f66fb846373dd1d18ea8255 Mon Sep 17 00:00:00 2001 From: Etancher <30360093+jahanvir@users.noreply.github.com> Date: Wed, 6 Sep 2023 18:21:02 +0530 Subject: [PATCH 7/9] added documentation for paLM AI --- docs/docs/examples/palmai-bot.md | 49 ++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 docs/docs/examples/palmai-bot.md diff --git a/docs/docs/examples/palmai-bot.md b/docs/docs/examples/palmai-bot.md new file mode 100644 index 00000000..26d73128 --- /dev/null +++ b/docs/docs/examples/palmai-bot.md @@ -0,0 +1,49 @@ +--- +sidebar_position: 2 +--- + +# Google PaLM AI bot + +This bot makes an API call to PaLMAI and processes the user input. It uses PaLM Chat. + +```py +import os +from textbase import bot, Message +from textbase.models import PalmAI +from typing import List + +# Load your PALM API key +# PALMAI.api_key = "" +# or from environment variable: +PalmAI.api_key = os.getenv("PALM_API_KEY") + +@bot() +def on_message(message_history: List[Message], state: dict = None): + + bot_response = PalmAI.generate( + message_history=message_history, # Assuming history is the list of user messages + ) + + response = { + "data": { + "messages": [ + { + "data_type": "STRING", + "value": bot_response + } + ], + "state": state + }, + "errors": [ + { + "message": "" + } + ] + } + + return { + "status_code": 200, + "response": response + } + +``` \ No newline at end of file From 891bdb6517ab5ad1c405d888d6500dcd005bed34 Mon Sep 17 00:00:00 2001 From: Etancher <30360093+jahanvir@users.noreply.github.com> Date: Thu, 7 Sep 2023 00:19:32 +0530 Subject: [PATCH 8/9] Replaced the extract_user_content_values with extract_content_values --- textbase/models.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/textbase/models.py b/textbase/models.py index 96c9d36e..33ff1692 100644 --- a/textbase/models.py +++ b/textbase/models.py @@ -27,14 +27,6 @@ def extract_content_values(message: Message): if content ] -# Returns user content if it's non empty. -def extract_user_content_values(message:Message): - return [ - content["content"] - for content in get_contents(message,"STRING") - if content and content["role"] == "user" - ] - class OpenAI: api_key = None @@ -170,7 +162,7 @@ def generate( for message in message_history: #list of all the contents inside a single message - contents = extract_user_content_values(message) + contents = extract_content_values(message) if contents: filtered_messages.extend(contents) From 58fa7f3659f372da9578f0714d745ea997de9f9e Mon Sep 17 00:00:00 2001 From: Kaustubh <139532137+kaus-cofactory@users.noreply.github.com> Date: Thu, 7 Sep 2023 23:36:57 +0530 Subject: [PATCH 9/9] piping out logs in the cli (#143) * piping out logs in the cli * add loading animation while fetching logs --- pyproject.toml | 1 + textbase/textbase_cli.py | 44 +++++++++++++++++++++++++++++++++++++++- textbase/utils/logs.py | 42 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 textbase/utils/logs.py diff --git a/pyproject.toml b/pyproject.toml index 8738793e..25a024e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ functions-framework = "^3.4.0" yaspin = "^3.0.0" pydantic = "^2.3.0" google-generativeai = "^0.1.0" +rich = "^13.5.2" [build-system] requires = ["poetry-core"] diff --git a/textbase/textbase_cli.py b/textbase/textbase_cli.py index e0651009..66c55534 100644 --- a/textbase/textbase_cli.py +++ b/textbase/textbase_cli.py @@ -9,6 +9,8 @@ import importlib.resources import re import urllib.parse +from textbase.utils.logs import fetch_and_display_logs + CLOUD_URL = "https://us-east1-chat-agents.cloudfunctions.net/deploy-from-cli" UPLOAD_URL = "https://us-east1-chat-agents.cloudfunctions.net/upload-file" @@ -71,7 +73,8 @@ def validate_bot_name(ctx, param, value): @click.option("--path", prompt="Path to the zip folder", required=True) @click.option("--bot_name", prompt="Name of the bot", required=True, callback=validate_bot_name) @click.option("--api_key", prompt="Textbase API Key", required=True) -def deploy(path, bot_name, api_key): +@click.option("--show_logs", is_flag=True, default=True, help="Fetch show_logs after deployment") +def deploy(path, bot_name, api_key, show_logs): click.echo(click.style(f"Deploying bot '{bot_name}' with zip folder from path: {path}", fg='yellow')) headers = { @@ -116,6 +119,23 @@ def deploy(path, bot_name, api_key): else: click.echo(click.style("Something went wrong! ❌", fg='red')) click.echo(response.text) + + # Piping logs in the cli in real-time + if show_logs: + click.echo(click.style(f"Fetching logs for bot '{bot_name}'...", fg='green')) + + cloud_url = f"{CLOUD_URL}/logs" + headers = { + "Authorization": f"Bearer {api_key}" + } + params = { + "botName": bot_name, + "pageToken": None + } + + fetch_and_display_logs(cloud_url=cloud_url, + headers=headers, + params=params) ################################################################################################################# @cli.command() @@ -223,6 +243,28 @@ def delete(bot_id, api_key): else: click.echo(click.style("Something went wrong!", fg='red')) + +@cli.command() +@click.option("--bot_name", prompt="Name of the bot", required=True) +@click.option("--api_key", prompt="Textbase API Key", required=True) +@click.option("--start_time", prompt="Logs for previous ___ minutes", required=False, default=5) +def logs(bot_name, api_key, start_time): + click.echo(click.style(f"Fetching logs for bot '{bot_name}'...", fg='green')) + + cloud_url = f"{CLOUD_URL}/logs" + headers = { + "Authorization": f"Bearer {api_key}" + } + params = { + "botName": bot_name, + "startTime": start_time, + "pageToken": None + } + + fetch_and_display_logs(cloud_url=cloud_url, + headers=headers, + params=params) + if __name__ == "__main__": cli() diff --git a/textbase/utils/logs.py b/textbase/utils/logs.py new file mode 100644 index 00000000..5ce7ff93 --- /dev/null +++ b/textbase/utils/logs.py @@ -0,0 +1,42 @@ +import requests +from rich.console import Console +from rich.table import Table +from rich.text import Text +import time +import click +from yaspin import yaspin + +def fetch_and_display_logs(cloud_url, headers, params): + console = Console() + table = Table(show_header=True, header_style="bold magenta") + table.add_column("Timestamp") + table.add_column("Severity") + table.add_column("Summary") + + while True: + with yaspin(text="Logs...", color="yellow") as spinner: + response = requests.get(cloud_url, headers=headers, params=params) + + if response.ok: + response_data = response.json() + data = response_data.get('data') + if data is not None: + logs = data.get('logs') + if logs: + for log in logs: + severity_color = 'blue' if log['severity'].lower() in ['notice', 'info', 'debug'] else 'red' if log['severity'].lower() in ['alert', 'critical', 'error'] else 'yellow' + + table.add_row(log['timestamp'], Text(log['severity'], style=severity_color), Text(log.get('text', ''), style=severity_color)) + + console.clear() + console.print(table) + # Update the params for the next request + params['pageToken'] = data.get('nextPageToken') + params['startTime'] = data.get('startTime') + else: + click.echo(click.style("No logs found in the response.", fg='yellow')) + else: + click.echo(click.style("Failed to retrieve logs.", fg='red')) + + # Poll the endpoint every 3 seconds + time.sleep(3) \ No newline at end of file