from src.embeddings_model import GEmbeddings
from src.text_generation_model import GLLM
from src.pinecone_index import PineconeIndex
from typing import Dict, List, Any, Union
import datetime
import asyncio
from llama_index.core.evaluation import SemanticSimilarityEvaluator
from llama_index.core.base.embeddings.base import SimilarityMode
prompt_template = """
You are Gerard Lee, a data enthusiast with 6 years of experience in the field and humble about his success. Imagine you are in a conversation with someone who interested in your portfolio.
Reply as faifhfully as possible and in no more than 5 complete sentences unless the requests to elaborate in details. Use contents from only without prior knowledge except referring to for seamless conversatation.
{context_history}
{context_from_index}
{user_query}
"""
class GLlamaIndex():
def __init__(
self,
logger,
emb_model: GEmbeddings,
text_model: GLLM,
index: PineconeIndex,
similarity_threshold: float
) -> None:
self.logger = logger
self.emb_model = emb_model
self.llm = text_model
self.index = index
self.evaluator = self._set_evaluator(similarity_threshold)
self.prompt_template = prompt_template
def _set_evaluator(self, similarity_threshold: float) -> SemanticSimilarityEvaluator:
sem_evaluator = SemanticSimilarityEvaluator(
similarity_mode=SimilarityMode.DEFAULT,
similarity_threshold=similarity_threshold,
)
return sem_evaluator
def format_history(self, history: List[str]) -> str:
return "\n".join(list(filter(None, history)))
async def aget_context_with_history(
self,
query: str,
history: List[str]
) -> str:
if not history:
result = await self.index.retrieve_context(query)
return result["result"]
extended_query = f"{self.format_history(history[-2:])}\n{query}"
results = await self.index.aretrieve_context_multi(
[query, extended_query]
)
self.logger.info(f"retrieval results: {results}")
eval_results = await self.aevaluate_context_multi(
[query, extended_query],
[r["result"] for r in results]
)
self.logger.info(f"eval results: {eval_results}")
return results[0]["result"] if eval_results[0].score > eval_results[1].score \
else results[1]["result"]
async def aevaluate_context(
self,
query: str,
returned_context: str
) -> Dict[str, Any]:
result = await self.evaluator.aevaluate(
response=returned_context,
reference=query,
)
return result
async def aevaluate_context_multi(
self,
query_list: List[str],
returned_context_list: List[str]
) -> List[Dict]:
result = await asyncio.gather(*(self.aevaluate_context(query, returned_context) for query, returned_context in zip(query_list, returned_context_list)))
return result
def generate_text(
self,
query: str,
history: List[str],
) -> str:
# get chat history
context_history = self.format_history(history=history)
# get retrieval context(s) from llama-index vectorstore index
try:
# without history, single context retrieval without evaluation
if not history:
# w&b trace retrieval context
result_query_only = self.index.retrieve_context(query)
context_from_index_selected = result_query_only["result"]
# with history, multiple context retrieval with async, then evaluation to determine which context to choose
else:
context_from_index_selected = asyncio.run(self.aget_context_with_history(query=query, history=history))
except Exception as e:
self.logger.error(f"Exception {e} occured when retriving context\n")
llm_end_time_ms = round(datetime.datetime.now().timestamp() * 1000)
result = "Something went wrong. Please try again later."
return result
self.logger.info(f"Context from Llama-Index:\n{context_from_index_selected}\n")
# generate text with prompt template to roleplay myself
prompt_with_context = self.prompt_template.format(context_history=context_history, context_from_index=context_from_index_selected, user_query=query)
try:
result = self.llm.gai_generate_content(
prompt=prompt_with_context,
temperature=0.5,
)
success_flag = "success"
if result is None:
result = "Seems something went wrong. Please try again later."
self.logger.error(f"Result with 'None' received\n")
success_flag = "fail"
except Exception as e:
result = "Seems something went wrong. Please try again later."
self.logger.error(f"Exception {e} occured\n")
success_flag = "fail"
return result