Spaces:
Runtime error
Runtime error
Commit
·
617c511
1
Parent(s):
90c9f68
Upload 5 files
Browse files- app.py +21 -27
- llm_client.py +31 -27
- pinecone_index.py +23 -28
app.py
CHANGED
|
@@ -4,19 +4,20 @@ import gradio as gr
|
|
| 4 |
|
| 5 |
from typing import List
|
| 6 |
|
| 7 |
-
from llm_client import
|
| 8 |
from pinecone_index import PinceconeIndex
|
| 9 |
|
| 10 |
-
SYSTEM_MESSAGE =
|
| 11 |
-
context and evidence and do not be verbose.
|
| 12 |
-
TOP_K =
|
| 13 |
|
| 14 |
|
| 15 |
def format_prompt(question: str, evidence: List[str]):
|
| 16 |
-
evidence_string =
|
| 17 |
for i, ev in enumerate(evidence):
|
| 18 |
-
evidence_string.join(f
|
| 19 |
|
|
|
|
| 20 |
content = f"{SYSTEM_MESSAGE} \
|
| 21 |
\n ### Question:{question} \
|
| 22 |
\n ### Evidence: {evidence_string} \
|
|
@@ -25,39 +26,36 @@ def format_prompt(question: str, evidence: List[str]):
|
|
| 25 |
return content
|
| 26 |
|
| 27 |
|
| 28 |
-
if __name__ ==
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
with open('config.yml', 'r') as file:
|
| 32 |
config = yaml.safe_load(file)
|
| 33 |
|
| 34 |
print(config)
|
| 35 |
|
| 36 |
-
data_path = config[
|
| 37 |
-
project = config[
|
| 38 |
|
| 39 |
-
index_name = config[
|
| 40 |
-
embedding_model = config[
|
| 41 |
-
embedding_dimension = config[
|
| 42 |
-
'embedding-dimension']
|
| 43 |
|
| 44 |
index = PinceconeIndex(index_name, embedding_model)
|
| 45 |
index.connect_index(embedding_dimension, False)
|
| 46 |
|
| 47 |
-
|
| 48 |
|
| 49 |
def get_answer(question: str):
|
| 50 |
evidence = index.query(question, top_k=TOP_K)
|
| 51 |
prompt_with_evidence = format_prompt(question, evidence)
|
| 52 |
print(prompt_with_evidence)
|
| 53 |
-
response =
|
| 54 |
final_output = [response] + evidence
|
| 55 |
|
| 56 |
return final_output
|
| 57 |
|
| 58 |
-
context_outputs = [gr.Textbox(label=f
|
| 59 |
-
|
| 60 |
-
result_output = [gr.Textbox(label='Answer')]
|
| 61 |
|
| 62 |
gradio_outputs = result_output + context_outputs
|
| 63 |
gradio_inputs = gr.Textbox(placeholder="Enter your question...")
|
|
@@ -69,12 +67,8 @@ if __name__ == '__main__':
|
|
| 69 |
outputs=gradio_outputs,
|
| 70 |
title="GT Student Code of Conduct Bot",
|
| 71 |
description="Get LLM-powered answers to questions about the \
|
| 72 |
-
|
| 73 |
-
from the Code of Conduct.
|
| 74 |
-
and this is an experimental setup. Please do not consider the \
|
| 75 |
-
answers as legal advice. We recommend you to consult the \
|
| 76 |
-
actual Student Code of Conduct online for authoritative \
|
| 77 |
-
information."
|
| 78 |
)
|
| 79 |
|
| 80 |
demo.launch()
|
|
|
|
| 4 |
|
| 5 |
from typing import List
|
| 6 |
|
| 7 |
+
from llm_client import GeminiClient
|
| 8 |
from pinecone_index import PinceconeIndex
|
| 9 |
|
| 10 |
+
SYSTEM_MESSAGE = "Give a precise answer to the question based on only the \
|
| 11 |
+
context and evidence and do not be verbose."
|
| 12 |
+
TOP_K = 1
|
| 13 |
|
| 14 |
|
| 15 |
def format_prompt(question: str, evidence: List[str]):
|
| 16 |
+
evidence_string = ""
|
| 17 |
for i, ev in enumerate(evidence):
|
| 18 |
+
evidence_string = evidence_string.join(f"\n Evidence {i+1}: {ev}")
|
| 19 |
|
| 20 |
+
print(f"evidence string - {evidence_string}")
|
| 21 |
content = f"{SYSTEM_MESSAGE} \
|
| 22 |
\n ### Question:{question} \
|
| 23 |
\n ### Evidence: {evidence_string} \
|
|
|
|
| 26 |
return content
|
| 27 |
|
| 28 |
|
| 29 |
+
if __name__ == "__main__":
|
| 30 |
+
config_path = "config.yml"
|
| 31 |
+
with open("config.yml", "r") as file:
|
|
|
|
| 32 |
config = yaml.safe_load(file)
|
| 33 |
|
| 34 |
print(config)
|
| 35 |
|
| 36 |
+
data_path = config["paths"]["data_path"]
|
| 37 |
+
project = config["paths"]["project"]
|
| 38 |
|
| 39 |
+
index_name = config["pinecone"]["index-name"]
|
| 40 |
+
embedding_model = config["sentence-transformers"]["model-name"]
|
| 41 |
+
embedding_dimension = config["sentence-transformers"]["embedding-dimension"]
|
|
|
|
| 42 |
|
| 43 |
index = PinceconeIndex(index_name, embedding_model)
|
| 44 |
index.connect_index(embedding_dimension, False)
|
| 45 |
|
| 46 |
+
gemini_client = GeminiClient()
|
| 47 |
|
| 48 |
def get_answer(question: str):
|
| 49 |
evidence = index.query(question, top_k=TOP_K)
|
| 50 |
prompt_with_evidence = format_prompt(question, evidence)
|
| 51 |
print(prompt_with_evidence)
|
| 52 |
+
response = gemini_client.generate_text(prompt_with_evidence)
|
| 53 |
final_output = [response] + evidence
|
| 54 |
|
| 55 |
return final_output
|
| 56 |
|
| 57 |
+
context_outputs = [gr.Textbox(label=f"Evidence {i+1}") for i in range(TOP_K)]
|
| 58 |
+
result_output = [gr.Textbox(label="Answer")]
|
|
|
|
| 59 |
|
| 60 |
gradio_outputs = result_output + context_outputs
|
| 61 |
gradio_inputs = gr.Textbox(placeholder="Enter your question...")
|
|
|
|
| 67 |
outputs=gradio_outputs,
|
| 68 |
title="GT Student Code of Conduct Bot",
|
| 69 |
description="Get LLM-powered answers to questions about the \
|
| 70 |
+
Georgia Tech Student Code of Conduct. The evidences are exerpts\
|
| 71 |
+
from the Code of Conduct.",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
)
|
| 73 |
|
| 74 |
demo.launch()
|
llm_client.py
CHANGED
|
@@ -1,41 +1,45 @@
|
|
| 1 |
import os
|
| 2 |
|
| 3 |
-
import google.generativeai as
|
| 4 |
|
| 5 |
|
| 6 |
-
class
|
| 7 |
def __init__(self):
|
| 8 |
self.connect_client()
|
| 9 |
|
| 10 |
def connect_client(self):
|
| 11 |
-
if
|
| 12 |
-
raise Exception(
|
| 13 |
-
|
| 14 |
-
api_key = os.getenv(
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
{"category": "
|
| 19 |
-
{"category": "
|
| 20 |
-
{
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
defaults = {
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
'top_p': 0.95,
|
| 32 |
-
'max_output_tokens': 1024,
|
| 33 |
-
'stop_sequences': [],
|
| 34 |
-
'safety_settings': safety_overrides,
|
| 35 |
}
|
| 36 |
|
| 37 |
-
self.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
|
| 39 |
def generate_text(self, prompt: str) -> str:
|
| 40 |
-
response =
|
| 41 |
-
return response.
|
|
|
|
| 1 |
import os
|
| 2 |
|
| 3 |
+
import google.generativeai as genai
|
| 4 |
|
| 5 |
|
| 6 |
+
class GeminiClient:
|
| 7 |
def __init__(self):
|
| 8 |
self.connect_client()
|
| 9 |
|
| 10 |
def connect_client(self):
|
| 11 |
+
if not os.getenv("GOOGLE_PALM_KEY"):
|
| 12 |
+
raise Exception("Please set your Google AI Studio key")
|
| 13 |
+
|
| 14 |
+
api_key = os.getenv("GOOGLE_PALM_KEY")
|
| 15 |
+
genai.configure(api_key=api_key)
|
| 16 |
+
|
| 17 |
+
safety_settings = [
|
| 18 |
+
{"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_ONLY_HIGH"},
|
| 19 |
+
{"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_ONLY_HIGH"},
|
| 20 |
+
{
|
| 21 |
+
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
| 22 |
+
"threshold": "BLOCK_ONLY_HIGH",
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
| 26 |
+
"threshold": "BLOCK_ONLY_HIGH",
|
| 27 |
+
},
|
| 28 |
+
]
|
| 29 |
|
| 30 |
defaults = {
|
| 31 |
+
"temperature": 0.7,
|
| 32 |
+
"top_k": 40,
|
| 33 |
+
"top_p": 0.95,
|
| 34 |
+
"max_output_tokens": 1024,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
}
|
| 36 |
|
| 37 |
+
self.model = genai.GenerativeModel(
|
| 38 |
+
model_name="gemini-pro",
|
| 39 |
+
generation_config=defaults,
|
| 40 |
+
safety_settings=safety_settings,
|
| 41 |
+
)
|
| 42 |
|
| 43 |
def generate_text(self, prompt: str) -> str:
|
| 44 |
+
response = self.model.generate_content(prompt)
|
| 45 |
+
return response.text
|
pinecone_index.py
CHANGED
|
@@ -19,18 +19,17 @@ class PinceconeIndex:
|
|
| 19 |
self.index_name = index_name
|
| 20 |
self._embeddingModel = HuggingFaceEmbeddings(model_name=model_name)
|
| 21 |
|
| 22 |
-
def connect_index(self, embedding_dimension: int,
|
| 23 |
-
delete_existing: bool = False):
|
| 24 |
index_name = self.index_name
|
| 25 |
|
| 26 |
# load pinecone env variables within Google Colab
|
| 27 |
-
if (not os.getenv(
|
| 28 |
-
dotenv_path = Path(
|
| 29 |
load_dotenv(dotenv_path=dotenv_path)
|
| 30 |
|
| 31 |
pinecone.init(
|
| 32 |
-
api_key=os.getenv(
|
| 33 |
-
environment=os.getenv(
|
| 34 |
)
|
| 35 |
|
| 36 |
if index_name in pinecone.list_indexes() and delete_existing:
|
|
@@ -47,9 +46,8 @@ class PinceconeIndex:
|
|
| 47 |
def upsert_docs(self, df: pd.DataFrame, text_col: str):
|
| 48 |
loader = DataFrameLoader(df, page_content_column=text_col)
|
| 49 |
docs = loader.load()
|
| 50 |
-
Pinecone.from_documents(docs, self._embeddingModel,
|
| 51 |
-
|
| 52 |
-
|
| 53 |
def get_embedding_model(self):
|
| 54 |
return self._embeddingModel
|
| 55 |
|
|
@@ -57,47 +55,44 @@ class PinceconeIndex:
|
|
| 57 |
return self.index_name
|
| 58 |
|
| 59 |
def query(self, query: str, top_k: int = 5) -> List[str]:
|
| 60 |
-
docsearch = Pinecone.from_existing_index(self.index_name,
|
| 61 |
-
self._embeddingModel)
|
| 62 |
res = docsearch.similarity_search(query, k=top_k)
|
| 63 |
|
| 64 |
return [doc.page_content for doc in res]
|
| 65 |
|
| 66 |
|
| 67 |
-
if __name__ ==
|
| 68 |
-
config_path =
|
| 69 |
-
with open(
|
| 70 |
config = yaml.safe_load(file)
|
| 71 |
|
| 72 |
print(config)
|
| 73 |
|
| 74 |
-
data_path = config[
|
| 75 |
-
project = config[
|
| 76 |
-
format =
|
| 77 |
|
| 78 |
-
index_name = config[
|
| 79 |
-
embedding_model = config[
|
| 80 |
-
|
| 81 |
-
embedding_dimension = config['sentence-transformers'][
|
| 82 |
-
'embedding-dimension']
|
| 83 |
delete_existing = True
|
| 84 |
|
| 85 |
-
if config[
|
| 86 |
print("Using manual chunking")
|
| 87 |
-
file_path_embedding = config[
|
| 88 |
-
df = pd.read_csv(file_path_embedding, header=None, names=[
|
| 89 |
else:
|
| 90 |
print("Using automatic chunking")
|
| 91 |
-
file_path_embedding = config[
|
| 92 |
df = pd.read_csv(file_path_embedding, index_col=0)
|
| 93 |
|
| 94 |
print(df)
|
| 95 |
start_time = time.time()
|
| 96 |
index = PinceconeIndex(index_name, embedding_model)
|
| 97 |
index.connect_index(embedding_dimension, delete_existing)
|
| 98 |
-
index.upsert_docs(df,
|
| 99 |
end_time = time.time()
|
| 100 |
-
print(f
|
| 101 |
|
| 102 |
index = PinceconeIndex(index_name, embedding_model)
|
| 103 |
index.connect_index(embedding_dimension, delete_existing=False)
|
|
|
|
| 19 |
self.index_name = index_name
|
| 20 |
self._embeddingModel = HuggingFaceEmbeddings(model_name=model_name)
|
| 21 |
|
| 22 |
+
def connect_index(self, embedding_dimension: int, delete_existing: bool = False):
|
|
|
|
| 23 |
index_name = self.index_name
|
| 24 |
|
| 25 |
# load pinecone env variables within Google Colab
|
| 26 |
+
if (not os.getenv("PINECONE_KEY")) or (not os.getenv("PINECONE_ENV")):
|
| 27 |
+
dotenv_path = Path("/content/gt-policy-bot/config.env")
|
| 28 |
load_dotenv(dotenv_path=dotenv_path)
|
| 29 |
|
| 30 |
pinecone.init(
|
| 31 |
+
api_key=os.getenv("PINECONE_KEY"),
|
| 32 |
+
environment=os.getenv("PINECONE_ENV"),
|
| 33 |
)
|
| 34 |
|
| 35 |
if index_name in pinecone.list_indexes() and delete_existing:
|
|
|
|
| 46 |
def upsert_docs(self, df: pd.DataFrame, text_col: str):
|
| 47 |
loader = DataFrameLoader(df, page_content_column=text_col)
|
| 48 |
docs = loader.load()
|
| 49 |
+
Pinecone.from_documents(docs, self._embeddingModel, index_name=self.index_name)
|
| 50 |
+
|
|
|
|
| 51 |
def get_embedding_model(self):
|
| 52 |
return self._embeddingModel
|
| 53 |
|
|
|
|
| 55 |
return self.index_name
|
| 56 |
|
| 57 |
def query(self, query: str, top_k: int = 5) -> List[str]:
|
| 58 |
+
docsearch = Pinecone.from_existing_index(self.index_name, self._embeddingModel)
|
|
|
|
| 59 |
res = docsearch.similarity_search(query, k=top_k)
|
| 60 |
|
| 61 |
return [doc.page_content for doc in res]
|
| 62 |
|
| 63 |
|
| 64 |
+
if __name__ == "__main__":
|
| 65 |
+
config_path = "config.yml"
|
| 66 |
+
with open("config.yml", "r") as file:
|
| 67 |
config = yaml.safe_load(file)
|
| 68 |
|
| 69 |
print(config)
|
| 70 |
|
| 71 |
+
data_path = config["paths"]["data_path"]
|
| 72 |
+
project = config["paths"]["project"]
|
| 73 |
+
format = ".csv"
|
| 74 |
|
| 75 |
+
index_name = config["pinecone"]["index-name"]
|
| 76 |
+
embedding_model = config["sentence-transformers"]["model-name"]
|
| 77 |
+
embedding_dimension = config["sentence-transformers"]["embedding-dimension"]
|
|
|
|
|
|
|
| 78 |
delete_existing = True
|
| 79 |
|
| 80 |
+
if config["paths"]["chunking"] == "manual":
|
| 81 |
print("Using manual chunking")
|
| 82 |
+
file_path_embedding = config["paths"]["manual_chunk_file"]
|
| 83 |
+
df = pd.read_csv(file_path_embedding, header=None, names=["chunks"])
|
| 84 |
else:
|
| 85 |
print("Using automatic chunking")
|
| 86 |
+
file_path_embedding = config["paths"]["auto_chunk_file"]
|
| 87 |
df = pd.read_csv(file_path_embedding, index_col=0)
|
| 88 |
|
| 89 |
print(df)
|
| 90 |
start_time = time.time()
|
| 91 |
index = PinceconeIndex(index_name, embedding_model)
|
| 92 |
index.connect_index(embedding_dimension, delete_existing)
|
| 93 |
+
index.upsert_docs(df, "chunks")
|
| 94 |
end_time = time.time()
|
| 95 |
+
print(f"Indexing took {end_time - start_time} seconds")
|
| 96 |
|
| 97 |
index = PinceconeIndex(index_name, embedding_model)
|
| 98 |
index.connect_index(embedding_dimension, delete_existing=False)
|