diff --git a/.env.example b/.env.example index 9d3d411..920cdee 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,15 @@ +# API Keys GOOGLE_API_KEY= OPENAI_API_KEY= + +# Postgres POSTGRES_USERNAME= -POSTGRES_PASSWORD= \ No newline at end of file +POSTGRES_PASSWORD= +POSTGRES_HOST= +POSTGRES_PORT= +POSTGRES_DATABASE= + +# Redis +REDIS_HOST= +REDIS_PORT= +REDIS_PASSWORD= \ No newline at end of file diff --git a/README.md b/README.md index 8eb8e90..04f5481 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ _Built right after [Unopened AI](https://github.com/waynemaranga/unopened_ai), i ![Shell](https://img.shields.io/badge/Shell_Script-121011?style=for-the-badge&logo=gnu-bash&logoColor=white) ![GPT](https://img.shields.io/badge/ChatGPT-74aa9c?style=for-the-badge&logo=openai&logoColor=white) ![Postgres](https://img.shields.io/badge/PostgreSQL-316192?style=for-the-badge&logo=postgresql&logoColor=white) +![Python]() +![Lua]() ## 📝 TODO diff --git a/bin/cli.py b/bin/cli.py new file mode 100644 index 0000000..838b224 --- /dev/null +++ b/bin/cli.py @@ -0,0 +1,52 @@ +import click # https://click.palletsprojects.com/en/8.1.x/ +from openai import OpenAI +from pathlib import Path # https://realpython.com/python-pathlib/ +from collections import Counter +from os import getenv +from dotenv import load_dotenv +import datetime + +load_dotenv() + +cwd: Path = Path.cwd() # current working dir +HOME_DIR: Path = Path.home() # user's home dir; OS-agnostic +cmd = Path(__file__).parent # current module's location; cmd != cwd +OPENAI_API_KEY = getenv("OPENAI_API_KEY") +counter_cwd = Counter(i for i in cwd.iterdir()) +counter_cmd = Counter(j.suffix for j in cmd.iterdir()) + + +client = OpenAI(api_key=OPENAI_API_KEY) + + +def respond(prompt: str, model="gpt-3.5-turbo") -> str: + """""" + if type(prompt) != str: + prompt = prompt.to_string() + + completion = client.chat.completions.create( + model=model, + max_tokens=1024, + temperature=0.2, + messages=[{"role": "system", "content": "You are a chatbot."}, + {"role": "user", "content": prompt}], # fmt: skip + ) + + return completion.choices[0].message.content + + +@click.command() +@click.option("--model", default="gpt-3.5-turbo", help="MODEL") +@click.option("--input", prompt="You", help="Ask Elizabot a question") +def chat(model, input): + # return respond(prompt=input, model=model) + # return respond(prompt=input) + click.echo(respond(model=model, prompt=input)) + + +if __name__ == "__main__": + # counter_home = Counter(j for j in HOME_DIR.iterdir()) + # print(counter_cwd) + # print(counter_cmd) + # print(respond("How old was Genghis Khan when he was born?")) + chat() diff --git a/lib/backend.dart b/lib/backend.dart index 7b4b6b1..4cac519 100644 --- a/lib/backend.dart +++ b/lib/backend.dart @@ -108,9 +108,8 @@ class Backend { String? postgresUsername = env['POSTGRES_USERNAME'] as String; String? postgresDatabase = env['POSTGRES_DATABASE'] as String; String? postgresHost = env['POSTGRES_HOST'] as String; // probably localhost - // int? postgresPort = env['POSTGRES_PORT'] as int; // probably 5432 - // int postgresPort = int.parse(dotenv.env['POSTGRES_PORT']!);// probably 5432 - int postgresPort = 5432; + int postgresPort = int.parse(env['POSTGRES_PORT']!); // probably 5432 + // int postgresPort = 5432; String? postgressPassword = env['POSTGRES_PASSWORD'] as String; /// ## `endpoint` diff --git a/lib/backend.py b/lib/backend.py new file mode 100644 index 0000000..8b81e42 --- /dev/null +++ b/lib/backend.py @@ -0,0 +1,88 @@ +from os import getenv +from dotenv import load_dotenv +import psycopg2 +import redis +from redis import StrictRedis + +load_dotenv() + +POSTGRES_USERNAME = getenv("POSTGRES_") +POSTGRES_PASSWORD = getenv("POSTGRES_") +POSTGRES_HOST = getenv("POSTGRES_") +POSTGRES_PORT = getenv("POSTGRES_") +POSTGRES_DATABASE = getenv("POSTGRES_") +REDIS_HOST = getenv("REDIS_HOST") +REDIS_PORT = getenv("REDIS_PORT") +REDIS_PASSWORD = getenv("REDIS_PASSWORD") + +# connection = psycopg2.connect(database=POSTGRES_DATABASE) # ... messy + + +class Chat: + def __init__(self, chat_id: str) -> None: + self.chat_id: str = chat_id + self.redis_client: StrictRedis = redis.StrictRedis( + host=REDIS_HOST, + port=REDIS_PORT, + password=REDIS_PASSWORD, + decode_responses=True, + ) + + def __str__(self) -> str: + return f"Chat ID: {self.chat_id}" + + def save_chat(self, message: str) -> None: + #! can be named better, save != store i.e in-memory + self.redis_client.rpush(self.chat_id, message) + + def get_conversation(self) -> list: + return self.redis_client.lrange(name=self.chat_id, start=0, end=-1) + + def finalize_conversation(self) -> None: + """Placeholder fn. for somethin mighty""" + conversation: list = self.get_conversation() + final_conversation: str = "\n".join( + conversation + ) # maybe XML transformation, like complicated joins + self.store_in_postgres(final_conversation) + self.redis_client.delete(self.chat_id) + + def store_in_postgres(self, final_conversation: str) -> None: + try: + connection = psycopg2.connect( + dbname=POSTGRES_DATABASE, + user=POSTGRES_USERNAME, + password=POSTGRES_PASSWORD, + host=POSTGRES_HOST, + port=POSTGRES_PORT, + ) + _cursorrr = connection.cursor() + _cursorrr.execute( + "INSERT INTO chats (chat_id, conversation) VALUES (%s, %s)", + (self.chat_id, final_conversation), + ) + connection.commit() + _cursorrr.close() + connection.close() + except (Exception, psycopg2.Error) as error: + print("Error while connecting to PostgreSQL", error) + + def count_bill(self) -> None: + conversation = self.get_conversation() + joined_conversation = "\n".join(conversation) + tokens = joined_conversation.split() + token_count = len(tokens) + cost_per_1000_tokens = 0.02 + cost = (token_count / 1000) * cost_per_1000_tokens + print(f"Total tokens used: {token_count}") + print(f"Total cost in USD: ${cost:.4f}") + return cost + + +if __name__ == "__main__": + test_chat = Chat(chat_id="test_chat_id") + test_chat.save_chat(message="Hello") + test_chat.save_chat(message="How are you?") + print(test_chat.get_conversation()) + test_chat.finalize_conversation() + test_chat.count_bill()