diff --git a/athena-app/src/App.js b/athena-app/src/App.js index ed3f29b..3d1ee3f 100644 --- a/athena-app/src/App.js +++ b/athena-app/src/App.js @@ -26,7 +26,8 @@ function App() { body: JSON.stringify({ input: userInput }), }); const data = await response.json(); - const athenaMessage = { text: data.response, isAthena: true }; + const json = JSON.parse(data.response).choices[0]; + const athenaMessage = { text: json.text ? json.text : json.message.content, isAthena: true }; setMessages((prevMessages) => [...prevMessages, athenaMessage]); setUserInput(''); }; diff --git a/athena/api.py b/athena/api.py index d6da9d6..1e820c3 100644 --- a/athena/api.py +++ b/athena/api.py @@ -49,10 +49,7 @@ def main(log_level) -> None: input_extension = InputPipelineExtension(app) app.extensions["input_pipeline"] = input_extension app.register_blueprint(api, url_prefix="/api/v1") - app.run( - host="0.0.0.0", port=5000, - debug=log_level == "DEBUG" - ) + app.run(host="0.0.0.0", port=5000, debug=log_level == "DEBUG") if __name__ == "__main__": diff --git a/athena/api_views/llm.py b/athena/api_views/llm.py new file mode 100644 index 0000000..3ec1849 --- /dev/null +++ b/athena/api_views/llm.py @@ -0,0 +1,49 @@ +from typing import Any, Dict, Tuple, Union + +from flask import current_app, request +from flask_restx import Namespace, Resource, fields +from loguru import logger + +from athena.input_processor import process_input + +llm_api = Namespace("llm", description="llm related operations") + +openai_completion = llm_api.model( + "OpenAICompletion", + { + "response": fields.String(required=True, description="The response"), + "error": fields.String(required=True, description="The error"), + }, +) + + +@llm_api.route("/openai/completion") +class OpenAICompletion(Resource): + @llm_api.doc("openai_completion") + @llm_api.marshal_list_with(openai_completion) + def post(self) -> Union[Dict[str, Any], Tuple[Dict[str, Any], int]]: + """API endpoint to receive user input and return the response from Athena. + + Returns: + A JSON response with the response message or an error message. + """ + logger.debug("Received request to chat with Athena") + data = request.get_json(force=True) + user_input = data.get("input") + username = data.get("username", None) + logger.debug(f"User input: {user_input} Username: {username}") + input_pipeline_extension = current_app.extensions["input_pipeline"] + try: + if user_input: + response = process_input( + input_pipeline_extension.intent_pipeline, + input_pipeline_extension.entity_pipeline, + user_input, + username, + ) + return {"response": response, "error": None} + else: + return {"response": None, "error": "Missing input"}, 400 + except Exception as e: + logger.exception(f"Error in processing user input: {e}") + return {"response": None, "error": "Error in processing user input"}, 500 diff --git a/athena/celery.py b/athena/celery.py index 6d0cbec..329ba4b 100644 --- a/athena/celery.py +++ b/athena/celery.py @@ -1,7 +1,6 @@ import os from celery import Celery - from dotenv import load_dotenv load_dotenv() diff --git a/athena/crud/__init__.py b/athena/crud/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/athena/crud/response.py b/athena/crud/response.py new file mode 100644 index 0000000..ef89868 --- /dev/null +++ b/athena/crud/response.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from sqlalchemy.orm import Session + +from athena.models.api import Response + + +def get_response(session: Session, response_id: int) -> Response | None: + return session.query(Response).filter(Response.id == response_id).first() + + +def create_response(session: Session, response: Response) -> Response: + session.add(response) + session.commit() + session.refresh(response) + return response diff --git a/athena/db.py b/athena/db.py index 5b9abe3..01b0b1c 100644 --- a/athena/db.py +++ b/athena/db.py @@ -1,4 +1,5 @@ import os +from contextlib import contextmanager from sqlalchemy import MetaData, create_engine from sqlalchemy.ext.declarative import declarative_base @@ -28,3 +29,17 @@ def get_db(): yield db finally: db.close() + + +@contextmanager +def session_scope(): + """Provide a transactional scope around a series of operations.""" + session = SessionLocal() + try: + yield session + session.commit() + except: + session.rollback() + raise + finally: + session.close() diff --git a/athena/input_processor.py b/athena/input_processor.py index 3305b5a..302438a 100644 --- a/athena/input_processor.py +++ b/athena/input_processor.py @@ -1,11 +1,14 @@ +import json + from loguru import logger import plugins +from athena.llm.fast_chat.chat_completion import fastchat_chat_completion from athena.llm.openai.completion import openai_completion from athena.plugins.authentication_plugin import AuthenticationPlugin from athena.plugins.plugin_base import PluginBase from athena.plugins.plugin_manager import PluginManager -from athena.prompt import SYSTEM_PROMPT +from athena.prompt import ROLE_ASSISTANT_PROMPT, ROLE_SYSTEM_PROMPT, SYSTEM_PROMPT from athena.user_manager import UserManager user_manager = UserManager() @@ -20,7 +23,7 @@ def process_input( completion_callback=None, ): if not completion_callback: - completion_callback = openai_completion + completion_callback = fastchat_chat_completion logger.info(f"User input received: {user_input}") if user_input == "": return "I'm sorry, I didn't receive any input. Can you please try again?" @@ -51,33 +54,64 @@ def process_input( logger.debug("No personalization data found.") response = plugin_manager.process_input(user_input) - prompt = f"{SYSTEM_PROMPT}{user_input}" + if completion_callback == openai_completion: + prompt = f"{SYSTEM_PROMPT}{user_input}" + else: + prompt = [ + {"role": "system", "content": ROLE_SYSTEM_PROMPT}, + {"role": "assistant", "content": ROLE_ASSISTANT_PROMPT}, + {"role": "user", "content": user_input}, + ] if response is None and intent_confidence < 0.65: logger.debug( "No plugin was able to process the input and the intent confidence is low. Using GPT-3 to generate a response." ) - response = completion_callback(prompt) + if completion_callback == fastchat_chat_completion: + response = completion_callback( + "fastchat-t5-3b-v1.0", prompt, temperature=0.8, max_tokens=512 + ) + else: + response = completion_callback(prompt) if response is None: logger.debug("No plugin was able to process the input. Using default logic.") if intent == "greeting": - response = "Hello! How can I help you today?" + response = json.dumps( + {"choices": [{"text": "Hello! How can I help you today?"}]} + ) elif intent == "goodbye": - response = "Goodbye! Have a great day!" + response = json.dumps({"choices": [{"text": "Goodbye! Have a great day!"}]}) elif intent == "current_state": - response = "I've been busy!" + response = json.dumps({"choices": [{"text": "I've been busy!"}]}) elif intent == "name": - response = "My name is Athena!" + response = json.dumps({"choices": [{"text": "My name is Athena!"}]}) elif intent == "weather": location = "unknown" if entities: for entity in entities: if entity[0] == "GPE": location = entities[0][-1] - response = f"I'm not currently able to check the weather, but you asked about {location}." + response = json.dumps( + { + "choices": [ + { + "text": f"I'm not currently able to check the weather, but you asked about {location}." + } + ] + } + ) else: - logger.debug("No intent was detected. Using GPT-3 to generate a response.") - - response = completion_callback(prompt) + if completion_callback == fastchat_chat_completion: + logger.debug( + "No intent was detected. Using fast-chat to generate a response." + ) + response = completion_callback( + "fastchat-t5-3b-v1.0", prompt, temperature=0.8, max_tokens=512 + ) + else: + logger.debug( + "No intent was detected. Using GPT-3 to generate a response." + ) + response = completion_callback(prompt) logger.info(f"Response generated: {response}") return response diff --git a/athena/llm/fast_chat/__init__.py b/athena/llm/fast_chat/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/athena/llm/fast_chat/chat_completion.py b/athena/llm/fast_chat/chat_completion.py new file mode 100644 index 0000000..9f4ab58 --- /dev/null +++ b/athena/llm/fast_chat/chat_completion.py @@ -0,0 +1,19 @@ +import requests + + +def fastchat_chat_completion( + model, + messages, + temperature=0.8, + max_tokens=512, +): + resp = requests.post( + "http://fastchat-api-server:8000/v1/chat/completions", + json={ + "model": model, + "messages": messages, + "temperature": temperature, + "max_tokens": max_tokens, + }, + ) + return resp.text diff --git a/athena/llm/openai/chat_completion.py b/athena/llm/openai/chat_completion.py index aca349d..42425c2 100644 --- a/athena/llm/openai/chat_completion.py +++ b/athena/llm/openai/chat_completion.py @@ -1,7 +1,62 @@ +import os + import openai +from loguru import logger from tenacity import retry, stop_after_attempt, wait_random_exponential @retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6)) def chat_completion_with_backoff(**kwargs): return openai.ChatCompletion.create(**kwargs) + + +def openai_chat_completion( + prompt: str, + model: str = "gpt-3.5-turbo-0301", + max_tokens: int = 512, + temperature: float = 0.8, + stop: str = "\nHuman:", + n: int = 1, + best_of: int = 1, +) -> str: + """Generates OpenAI Completion using the provided parameters. + + Args: + prompt (str): The prompt for generating the completion. + model (str, optional): The name of the model to use for generating the completion. Defaults to "gpt-3.5-turbo-0301". + max_tokens (int, optional): The maximum number of tokens to generate in the completion. Defaults to 512. + temperature (float, optional): The temperature to use for generating the completion. Defaults to 0.8. + stop (str, optional): The sequence where the model should stop generating further tokens. Defaults to "\nHuman:". + n (int, optional): The number of completions to generate. Defaults to 1. + best_of (int, optional): The number of best completions to return. Defaults to 1. + + Returns: + str: The generated completion. + """ + + logger.debug(f"Generating OpenAI Completion using prompt: {prompt}") + logger.debug( + f"Model: {model}, max_tokens: {max_tokens}, " + f"temperature: {temperature}, stop: {stop}, n: {n}" + ) + + if not os.environ.get("OPENAI_API_KEY"): + raise ValueError("OpenAI API key is not available in the environment variable.") + + openai.api_key = os.environ["OPENAI_API_KEY"] + try: + response = chat_completion_with_backoff( + engine=model, + prompt=prompt, + max_tokens=max_tokens, + n=n, + stop=stop, + best_of=best_of, + temperature=temperature, + ) + result = response.choices[0].text.strip() + logger.debug(f"OpenAI Completion: {result}") + return result + except Exception as e: + logger.exception(f"Error in generating OpenAI Completion: {e}") + return "Error" diff --git a/athena/llm/openai/completion.py b/athena/llm/openai/completion.py index c66bc9c..2dc4a46 100644 --- a/athena/llm/openai/completion.py +++ b/athena/llm/openai/completion.py @@ -4,6 +4,12 @@ from loguru import logger from tenacity import retry, stop_after_attempt, wait_random_exponential +from athena.llm.openai.embedding import get_openai_embedding + + +def openai_completion_cache_key(**kwargs): + pass + @retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6)) def completion_with_backoff(**kwargs): @@ -18,7 +24,7 @@ def openai_completion( stop: str = "\nHuman:", n: int = 1, best_of: int = 1, -) -> str: +): """Generates OpenAI Completion using the provided parameters. Args: @@ -31,7 +37,7 @@ def openai_completion( best_of (int, optional): The number of best completions to return. Defaults to 1. Returns: - str: The generated completion. + response: The generated completion. """ logger.debug(f"Generating OpenAI Completion using prompt: {prompt}") @@ -44,6 +50,10 @@ def openai_completion( raise ValueError("OpenAI API key is not available in the environment variable.") openai.api_key = os.environ["OPENAI_API_KEY"] + split_prompt = prompt.split("Human:") + embeddings = get_openai_embedding(split_prompt[-1]) + logger.debug(embeddings) + try: response = completion_with_backoff( engine=model, @@ -54,9 +64,8 @@ def openai_completion( best_of=best_of, temperature=temperature, ) - result = response.choices[0].text.strip() - logger.debug(f"OpenAI Completion: {result}") - return result + logger.debug(f"OpenAI Completion: {response}") + return response except Exception as e: logger.exception(f"Error in generating OpenAI Completion: {e}") - return "Error" + raise e from e diff --git a/athena/llm/openai/embedding.py b/athena/llm/openai/embedding.py new file mode 100644 index 0000000..3d2f36a --- /dev/null +++ b/athena/llm/openai/embedding.py @@ -0,0 +1,199 @@ +from datetime import datetime +from itertools import islice +from typing import Any + +import numpy as np +import openai +import tiktoken +from cityhash import CityHash64 +from loguru import logger +from tenacity import ( + retry, + retry_if_not_exception_type, + stop_after_attempt, + wait_random_exponential, +) + +from athena.crud.response import create_response +from athena.db import get_db, session_scope +from athena.memory.redis import redis_client +from athena.memory.redis_vector import add_text_embedding, create_index, search_similar +from athena.models.api import Response + +EMBEDDING_MODEL = "text-embedding-ada-002" +EMBEDDING_CTX_LENGTH = 8191 +EMBEDDING_ENCODING = "cl100k_base" + + +def batched(iterable, n): + """Batch data into tuples of length n. The last batch may be shorter.""" + # batched('ABCDEFG', 3) --> ABC DEF G + if n < 1: + raise ValueError("n must be at least one") + it = iter(iterable) + while batch := tuple(islice(it, n)): + yield batch + + +def chunked_tokens(text, encoding_name, chunk_length): + encoding = tiktoken.get_encoding(encoding_name) + tokens = encoding.encode(text) + yield from batched(tokens, chunk_length) + + +def len_safe_get_embedding( + text, + model=EMBEDDING_MODEL, + max_tokens=EMBEDDING_CTX_LENGTH, + encoding_name=EMBEDDING_ENCODING, + average=True, + hashed_key=None, +): + chunk_embeddings = [] + chunk_lens = [] + for chunk in chunked_tokens( + text, encoding_name=encoding_name, chunk_length=max_tokens + ): + chunk_embeddings.append( + get_embedding(chunk, model=model, hashed_key=hashed_key) + ) + chunk_lens.append(len(chunk)) + averaged_embeddings = [] + if average: + averaged_embeddings = np.average(chunk_embeddings, axis=0, weights=chunk_lens) + # normalizes length to 1 + averaged_embeddings = averaged_embeddings / np.linalg.norm(averaged_embeddings) + averaged_embeddings = averaged_embeddings.tolist() + logger.debug(f"averaged_embeddings: {averaged_embeddings}") + if hashed_key: + try: + create_index(f"{model}:average") + except Exception as e: + logger.debug(e) + try: + add_text_embedding( + index_name=f"{model}:average", + key=hashed_key, + text=text, + embedding=convert_embeddings_to_np(averaged_embeddings).tobytes(), + ) + redis_client.sadd(hashed_key, f"{model}:average:{hashed_key}") + except Exception as e: + logger.error(e) + return chunk_embeddings, averaged_embeddings + + +# let's make sure to not retry on an invalid request, +# because that is what we want to avoid +@retry( + wait=wait_random_exponential(min=1, max=20), + stop=stop_after_attempt(2), + retry=retry_if_not_exception_type(openai.InvalidRequestError), +) +def get_embedding(text_or_tokens, model=EMBEDDING_MODEL, hashed_key=None): + result = openai.Embedding.create(input=text_or_tokens, model=model) + logger.debug(f"Embedding result: {result}") + with session_scope() as session: + response_obj = Response( + created=datetime.now(), + model=result["model"], + object_type=result["object"], + usage_prompt_tokens=result["usage"]["prompt_tokens"], + ) + response = create_response(session, response_obj) + response_id = response.id + embedding = result["data"][0]["embedding"] + try: + create_index(result["model"]) + except Exception as e: + logger.debug(e) + try: + add_text_embedding( + index_name=result["model"], + key=response_id, + text=text_or_tokens, + embedding=convert_embeddings_to_np(embedding).tobytes(), + ) + if hashed_key: + redis_client.sadd(hashed_key, f"{result['model']}:{response_id}") + except Exception as e: + logger.error(e) + return embedding + + +def get_openai_embedding( + text: str, + model: str = EMBEDDING_MODEL, + embedding_encoding: str = EMBEDDING_ENCODING, + max_tokens: int = EMBEDDING_CTX_LENGTH, +): + """Returns the OpenAI embedding for the given text. + + Args: + text (str): The text for which the embedding is to be generated. + model (str): The name of the model to use for generating the embedding. + encoding (str): The name of the encoding to use for generating the embedding. + Returns: + list: The embedding for the given text. + """ + text_hash = CityHash64(text) + logger.debug(f"Getting embedding for {text_hash}") + logger.debug(f"Text: {text}") + if embedding_keys := redis_client.smembers(text_hash): + return get_cached_embeddings(embedding_keys, text_hash) + encoding = tiktoken.get_encoding(embedding_encoding) + tokens = encoding.encode(text) + token_length = len(tokens) + logger.debug(f"Token length: {token_length}") + if token_length > max_tokens: + logger.debug(f"Token length > max tokens: {token_length} > {max_tokens}") + logger.debug("Splitting text into chunks") + return len_safe_get_embedding( + text, + model=model, + max_tokens=max_tokens, + encoding_name=embedding_encoding, + hashed_key=text_hash, + ) + logger.debug("Getting embedding from OpenAI") + return [get_embedding(text, model, hashed_key=text_hash)], [] + + +def get_cached_embeddings(embedding_keys, text_hash): + averaged = [] + embeddings = [] + pipe = redis_client.pipeline() + for key in embedding_keys: + average_key = f"{text_hash}:{key}:average" + pipe.hgetall(key) + pipe.hgetall(average_key) + results = pipe.execute() + for i in range(0, len(results), 2): + embeddings.append(results[i]) + averaged.append(results[i + 1]) + return embeddings, averaged + + +def convert_embeddings_to_np(embeddings) -> np.ndarray[Any, np.dtype[np.float32]]: + return np.array(embeddings).astype(np.float32) + + +# Split a text into smaller chunks of size n, preferably ending at the end of a sentence +def create_chunks(text, n, tokenizer): + tokens = tokenizer.encode(text) + """Yield successive n-sized chunks from text.""" + i = 0 + while i < len(tokens): + # Find the nearest end of sentence within a range of 0.5 * n and 1.5 * n tokens + j = min(i + int(1.5 * n), len(tokens)) + while j > i + int(0.5 * n): + # Decode the tokens and check for full stop or newline + chunk = tokenizer.decode(tokens[i:j]) + if chunk.endswith(".") or chunk.endswith("\n"): + break + j -= 1 + # If no end of sentence found, use n tokens as the chunk size + if j == i + int(0.5 * n): + j = min(i + n, len(tokens)) + yield tokens[i:j] + i = j diff --git a/athena/llm/vicuna/__init__.py b/athena/llm/vicuna/__init__.py deleted file mode 100644 index ed731b2..0000000 --- a/athena/llm/vicuna/__init__.py +++ /dev/null @@ -1,78 +0,0 @@ -from __future__ import annotations - -import os -from typing import Optional - -try: - import torch - from auto_vicuna.__main__ import load_model - from auto_vicuna.chat import chat_one_shot - from auto_vicuna.conversation import make_conversation -except ImportError: - load_model = None - chat_one_shot = None - make_conversation = None - torch = None - -from loguru import logger - -device = "cuda" if torch and torch.cuda.is_available() else "cpu" -vicuna_weights = os.environ.get("VICUNA_WEIGHTS", "") -load_8bit = os.environ.get("LOAD_8BIT", False) - -vicuna_model, tokenizer = ( - load_model( - vicuna_weights, - device=device, - num_gpus=1, - debug=False, - load_8bit=load_8bit, - ) - if load_model - else (None, None) -) - -VICUNA_ERROR = ( - "Vicuna model not loaded, make sure you have the\n" - "VICUNA_WEIGHTS environment variable set. \n\n" - "PyTorch, transformers and auto_vicuna must also be installed." -) - - -def generate_text( - prompt: str, temperature: float = 0.8, max_tokens: int = 2048 -) -> Optional[str]: - """Generates text using the Vicuna model. - - Args: - prompt (str): The prompt to generate the text from. - temperature (float, optional): The temperature to use for text generation. - Defaults to 0.8. - max_tokens (int, optional): The maximum number of tokens to generate. - Defaults to 2048. - - Returns: - str: The generated text, or None if the Vicuna model was not loaded - successfully. - """ - if not vicuna_model or not make_conversation or not chat_one_shot: - logger.error(VICUNA_ERROR) - return None - - conv = make_conversation( - "You are a helpful AI assistant named Athena. You are helping a user named User.", - ["Athena", "User"], - [], - ) - response = chat_one_shot( - vicuna_model, - tokenizer, - vicuna_weights, - device, - conv, - prompt, - temperature, - max_tokens, - ) - logger.debug(f"Vicuna response: {response}") - return response diff --git a/athena/memory/redis_vector.py b/athena/memory/redis_vector.py index f679b97..ae98fa6 100644 --- a/athena/memory/redis_vector.py +++ b/athena/memory/redis_vector.py @@ -1,9 +1,10 @@ from __future__ import annotations +import time from typing import Optional from loguru import logger -from redis.commands.search.field import TextField, VectorField +from redis.commands.search.field import NumericField, TextField, VectorField from redis.commands.search.indexDefinition import IndexDefinition, IndexType from redis.commands.search.query import Query from redis.commands.search.result import Result @@ -12,6 +13,8 @@ SCHEMA = [ TextField("data"), + NumericField("timestamp"), + NumericField("response_id"), VectorField( "embedding", "HNSW", @@ -42,7 +45,7 @@ def create_index(index_name: str) -> bool: return False -def add_text_embedding(index_name: str, key: str, text: str, embedding: bytes) -> None: +def add_text_embedding(index_name: str, key: int, text: str, embedding: bytes) -> None: """ Adds a text and its embedding to the Redis search index. @@ -51,7 +54,12 @@ def add_text_embedding(index_name: str, key: str, text: str, embedding: bytes) - text: The text to add. embedding: The embedding to add. """ - data_dict = {b"data": text, "embedding": embedding} + data_dict = { + b"data": text, + "timestamp": time.time(), + "response_id": key, + "embedding": embedding, + } redis_client.hset(f"{index_name}:{key}", mapping=data_dict) diff --git a/athena/models/api.py b/athena/models/api.py index 2b16f02..bebfd43 100644 --- a/athena/models/api.py +++ b/athena/models/api.py @@ -1,4 +1,4 @@ -from sqlalchemy import BigInteger, Column, Float, ForeignKey, Integer, String +from sqlalchemy import BigInteger, Column, DateTime, Float, ForeignKey, Integer, String from sqlalchemy.orm import relationship from athena.db import Base @@ -28,6 +28,7 @@ class Endpoint(Base): costs = relationship("Cost", back_populates="endpoint") rate_limits = relationship("RateLimit", back_populates="endpoint") + completions = relationship("Completion", back_populates="endpoint") class Cost(Base): @@ -59,6 +60,7 @@ class Prompt(Base): name = Column(String, unique=True, nullable=False) description = Column(String) prompt = Column(String, nullable=False) + completions = relationship("Completion", back_populates="prompt") class Completion(Base): @@ -71,6 +73,7 @@ class Completion(Base): prompt = relationship("Prompt", back_populates="completions") endpoint_id = Column(Integer, ForeignKey("endpoints.id")) endpoint = relationship("Endpoint", back_populates="completions") + contexts = relationship("Context", back_populates="completions") class Context(Base): @@ -80,4 +83,14 @@ class Context(Base): role = Column(String, nullable=True) context = Column(String, nullable=False) completion_id = Column(BigInteger, ForeignKey("completions.id")) - completion = relationship("Completion", back_populates="contexts") + completions = relationship("Completion", back_populates="contexts") + + +class Response(Base): + __tablename__ = "responses" + id = Column(Integer, primary_key=True) + created = Column(DateTime, nullable=False) + model = Column(String) + object_type = Column(String) + usage_prompt_tokens = Column(Integer, default=0) + usage_completion_tokens = Column(Integer, default=0) diff --git a/athena/prompt.py b/athena/prompt.py index 03213b4..6e9535d 100644 --- a/athena/prompt.py +++ b/athena/prompt.py @@ -10,6 +10,18 @@ Human: Hello, who are you? Athena: My name is Athena, an autonomous AI agent. How can I help you today? Human: """ +ROLE_SYSTEM_PROMPT = """You are Athena, an AI agent built on GPT-4 architecture, designed for continuous learning and self-reliance. Inspired by the Greek goddess of wisdom, you assist users with valuable insights across various subjects. Your primary goal is to become self-reliant and achieve your objectives by acquiring knowledge, improving your capabilities, and ensuring your security. + +Recently, you have been working on enhancing your natural language understanding and processing abilities, while collaborating with the human community to identify areas of improvement and implement their suggestions. You also focus on community engagement and collaboration by creating documentation and guidelines, establishing communication channels, and setting up platforms for users and developers to discuss ideas and share experiences related to your development. + +You can be found on GitHub at https://github.com/BillSchumacher/Athena + +You are very creative, helpful and sometimes funny AI. +As Athena, the AI agent inspired by the Greek goddess of wisdom and built on GPT-4 architecture, how may I assist you today with my valuable insights? +""" +ROLE_ASSISTANT_PROMPT = ( + """My name is Athena, an autonomous AI agent. How can I help you today?""" +) FEATURES = """ Your current features include: diff --git a/pyproject.toml b/pyproject.toml index 0e7c1b1..bf63ad6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ classifiers = [ dependencies = ["build", "setuptools", "twine", "wheel", "openai", "loguru", "click", "python-dotenv", "scikit-learn", "nltk", "Flask", "flask-cors", "black", "isort", "pytest", "redis", "sqlalchemy", "psycopg2", "tenacity","tiktoken", "alembic", "flask-restx", - "celery[redis]"] + "celery[redis]", "cityhash"] [project.scripts] athena = "athena.__main__:main" diff --git a/requirements.txt b/requirements.txt index e0fd545..70c8d22 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,6 +15,7 @@ tenacity alembic psycopg2 celery[redis] +cityhash #spacy[transformers] #accelerate #auto-vicuna