import uuid
from embedding_loader import *
from initialize_db import QdrantClientInitializer
from pdf_loader import PDFLoader
from IPython.display import display, Markdown
import gradio as gr
from langchain_core.messages import HumanMessage, AIMessage
from langchain.memory import ConversationBufferMemory
from langchain_core.chat_history import InMemoryChatMessageHistory
from qdrant_client import QdrantClient, models
from db_operations import DatabaseOperations
from openai import AzureOpenAI
import json
from qdrant_client.http import models as rest
import time
from fastembed.sparse.bm25 import Bm25
from fastembed.late_interaction import LateInteractionTextEmbedding
from pydantic import BaseModel, Field
from langchain_core.load import dumpd, dumps, load, loads
from langchain.schema import Document
dense_embedding_model = import_embedding()
late_interaction_embedding_model = LateInteractionTextEmbedding("colbert-ir/colbertv2.0")
bm25_embedding_model = Bm25("Qdrant/bm25", language="turkish")
AZURE_OPENAI_KEY = os.getenv('azure_api')
os.environ['AZURE_OPENAI_KEY'] = AZURE_OPENAI_KEY
openai.api_version = "2024-08-01-preview" # change it with your own version
openai.azure_endpoint = os.getenv('azure_endpoint')
model = "gpt-4o-mini" # deployment name on Azure OPENAI Studio
client = AzureOpenAI(azure_endpoint = openai.azure_endpoint,
api_key=AZURE_OPENAI_KEY,
api_version=openai.api_version)
obj_qdrant = QdrantClientInitializer()
qclient = obj_qdrant.initialize_db()
obj_loader = PDFLoader()
encoding = tiktoken.encoding_for_model('gpt-4o-mini')
# -----
def retriever_db(client, query, collection_name, CAR_ID):
dense_query_vector = list(dense_embedding_model.embed_documents([query]))[0]
sparse_query_vector = list(bm25_embedding_model.query_embed(query))[0]
late_query_vector = list(late_interaction_embedding_model.query_embed(query))[0].tolist()
prefetch = [
models.Prefetch(
query=dense_query_vector,
using="sfr-mistral",
limit=20,
),
models.Prefetch(
query=models.SparseVector(**sparse_query_vector.as_object()),
using="bm25",
limit=20,
),
models.Prefetch(
query=late_query_vector,
using="colbertv2.0",
limit=20,
),
]
results = client.query_points(
collection_name,
prefetch=prefetch,
query=models.FusionQuery(
fusion=models.Fusion.RRF,
),
with_payload=True,
query_filter=models.Filter(
must=[
models.FieldCondition(key="car_id", match=models.MatchValue(value=CAR_ID))
]),
limit=5,
)
# retrieved_chunks = [doc.payload for doc in results.points]
retrieved_chunks = [Document(metadata=doc.payload["metadata"], page_content=doc.payload["page_content"]) for doc in results.points]
return retrieved_chunks
## new version
def chat_gpt(prompt=None, history=[], model=model, client=client, tools=[None]):
if prompt is None:
messages = history
else:
history.append({"role": "user", "content": f"{prompt}"})
messages = history
completion = client.chat.completions.create(
model=model,
messages=messages,
tools=tools,
tool_choice="required",
temperature=0.0
)
return completion
retrieval_functions = [
{
"type": "function",
"function":{
"name": "get_section_titles",
"description": """Use this function to get the section, subsection and subsubsection titles from a user manual table of content.""",
"parameters": {
"type": "object",
"properties": {
"section_title": {
"type": "string",
"description": "Title of the section in the table of content",
},
"sub_section_title": {
"type": "string",
"description": "Title of the subsection in the table of content",
},
"sub_sub_section_title": {
"type": "string",
"description": "Title of the subsubsection in the table of content",
}
},
"required": ["section_title", "sub_section_title", "sub_sub_section_title"],
}
}
}
]
def get_section_content(section_title, sub_section_title, sub_sub_section_title, content_path):
with open(content_path, "r") as file:
doc_section_content = json.loads(file.read())
response = None
try:
content = doc_section_content["TableOfContents"][section_title][sub_section_title][sub_sub_section_title]["content"]
pages = doc_section_content["TableOfContents"][section_title][sub_section_title][sub_sub_section_title]["pages"]
response = Document(metadata={"page": pages,
"section_title": section_title,
"sub_section_title": sub_section_title,
"sub_sub_section_title": sub_sub_section_title},
page_content = content)
except:
pass
return response
def get_lead_result(question):
hizmet_listesi = {"Bakım": """Check-Up, Periyodik Bakım, Aks Değişimi, Amortisör Değişimi, Amortisör Takozu Değişimi, Baskı Balata Değişimi, Benzin Filtresi Değişimi,
Debriyaj Balatası Değişimi, Direksiyon Kutusu Değişimi, Dizel Araç Bakımı, Egzoz Muayenesi, Fren Kaliperi Değişimi, El Freni Teli Değişimi,
Fren Balatası Değişimi, Fren Disk Değişimi, Hava Filtresi Değişimi, Helezon Yay Değişimi, Kampana Fren Balatası Değişimi,
Kızdırma Bujisi Değişimi, Rot Başı Değişimi, Rot Kolu Değişimi, Rotil Değişimi, Silecek Değişimi, Süspansiyon, Triger Kayışı Değişimi,
Triger Zinciri Değişimi, V Kayışı Değişimi, Yağ Filtresi Değişimi, Yakıt Filtresi Değişimi, Havayastığı Değişimi""",
"Yağ ve Sıvılar": """Şanzıman Yağı Değişimi, Dizel Araçlarda Yağ Değişimi, Yağ Değişimi, Fren Hidrolik Değişimi, Antifriz Değişimi,""",
"Akü": """Akü Şarj Etme, Akü Değişimi""",
"Klima": """Oto Klima Kompresörü Tamiri, Oto Klima Tamiri, Araç Klima Temizliği, Araç Klima Bakteri Temizliği, Klima Gazı Dolumu, Klima Dezenfeksiyonu, Polen Filtresi Değişimi""",
"Elektrik": """Servis Uyarı Lambası Sıfırlama,Buji Kablosu Değişimi, Arıza Tespit, Göstergelerin Kontrolü, Far Ayarı ve Ampul Değişimi, Buji Değişimi, Sigorta Değişimi""",
"Lastik/ Jant": """Lastik Jant Satış, Lastik Değişimi, Balans Ayarı, Rot Ayarı, Rotasyon, Lastik Tamiri, Hava Kontrolü, Nitrojen Dolumu, Supap Değişimi, Lastik Saklama (Lastik Oteli), Jant Sökme Takma,""",
"Diğer": """Cam Tamiri, Egzoz Sorunları""",
"Hibrit Araçlar": "Hibrit Araç Aküsü"}
lead_functions = [
{
"type": "function",
"function": {
"name": "grade_service_relevance",
"description": "Grade the relevance of services to a user question",
"parameters": {
"type": "object",
"properties": {
"binary_score": {
"type": "string",
"description": "Services are relevant to the question, 'yes' or 'no'",
"enum": ["yes", "no"]
}
},
"required": ["binary_score"]
}
}
}
]
# System message
system_message = """Soruyu cevaplarken:
1- Önce soruyu düşün.
2- Kullanıcının sorduğu soru, hizmet listesinde sunulan hizmetlerle alakalı mı?
Alakalı ise "yes", değilse "no" olarak cevap ver."""
def service_grader_relevance(hizmet_listesi: str, question: str) -> dict:
completion = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": system_message},
{"role": "user", "content": f"Provided services: \n\n {hizmet_listesi} \n\n User question: {question}"}
],
tools=lead_functions,
tool_choice={"type": "function", "function": {"name": "grade_service_relevance"}}
)
tool_call = completion.choices[0].message.tool_calls[0]
return json.loads(tool_call.function.arguments)
result = service_grader_relevance(hizmet_listesi, question)
return result['binary_score']
def chat_gpt_nofn(prompt=None, history=[], model=model, client=client):
if prompt is None:
messages = history
else:
history.append({"role": "user", "content": f"{prompt}"})
messages = history
completion = client.chat.completions.create(
model=model,
messages=messages,
stream=False)
return completion
def format_chat_prompt(chat_history):
prompt = []
print(chat_history)
for turn in chat_history:
user_message, ai_message = turn
prompt.append({"role": "user", "content": user_message})
prompt.append({"role": "assistant", "content": ai_message})
return prompt
class GradeDocuments(BaseModel):
"""Binary score for relevance check on retrieved documents."""
binary_score: str = Field(description="Documents are relevant to the question, 'yes' or 'no'.")
def grade_document_with_openai(document: str, question: str) -> GradeDocuments:
# system_message = """
# You are a grader assessing relevance of a retrieved document to a user question.
# Consider the following when making your assessment:
# - Does the document contain information that directly addresses the core problem or main topic in the user's question?
# - Is there a meaningful semantic relationship between the question's main inquiry and the document's content?
# - Do the solutions, explanations or recommendations in the document specifically relate to the problem stated in the question?
# If the document provides information that helps understand or solve the specific problem asked in the question (not just containing related terms or general context), grade it as relevant.
# Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question.
# """
system_message = """
You are a grader assessing relevance of a retrieved document to a user question.
Consider the following when making your assessment:
- Does the document directly or indiretly address the user's question?
- Does it provide information or context that is pertinent to the question?
- Does it discuss relevant risks, benefits, recommendations, or considerations related to the question?
If the document contains keyword(s) or semantic meaning (in Turkish) related or partially related to the question, grade it as relevant.
Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question.
"""
# system_message = """You are a grader evaluating the relevance of a document to a user question. Assess based on these criteria:
# - Does the document directly or indirectly address the question?
# - Does it provide pertinent information, context, or considerations (e.g., risks, benefits, recommendations)?
# - If the document contains related keywords or semantic meaning (in Turkish), consider it relevant.
# Provide a binary score ('yes' for relevant, 'no' for not relevant). Use 90% relevance as the threshold for 'yes.'"""
# response = client.chat.completions.create(
# model=model,
# messages=[
# {"role": "system", "content": system_message},
# {"role": "user", "content": f'Retrieved document:\n-------------------\n{document}\n User Question:\n--------------\n{question}'}
# ]
# )
response = client.beta.chat.completions.parse(
model=model,
messages=[
{"role": "system", "content": system_message},
{"role": "user", "content": f'Retrieved document:\n-------------------\n{document}\n User Question:\n--------------\n{question}'}
],
response_format=GradeDocuments
)
score = response.choices[0].message.parsed
# score = response.choices[0].message.content.strip()
print("GRADE:", score)
# return GradeDocuments(binary_score=score)
return score.binary_score
### Hallucination Grader
# Data model
# class GradeHallucinations(BaseModel):
# """Binary score for hallucination present in generation answer."""
# binary_score: str = Field(description="Don't consider calling external APIs for additional information. Answer is supported by the facts and related to the question, 'yes' or 'no'.")
# explanation: str = Field(
# description="Provide a detailed explanation for the binary score, including reasoning and evidence supporting the decision."
# )
# reproduced_answer: str = Field(description=" - If the binary score 'no': Give a new answer that is supported by the facts.\n- If the binary score 'yes': Give no new answer.")
class GradeHallucinations(BaseModel):
"""Binary score for hallucination present in generation answer."""
binary_score: str = Field(description="Don't consider calling external APIs for additional information. Answer is supported by the facts and related to the question, 'yes' or 'no'.")
explanation: str = Field(
description="Provide a detailed explanation for the binary score, including reasoning and evidence supporting the decision."
)
def check_hallucinations(document: str, history_document: str, generation: str, question: str) -> GradeHallucinations:
system_message = f"""
You are a grader tasked with evaluating whether an LLM generation is fully supported by a set of retrieved facts and related to the question. Follow these rules strictly:
1. If the LLM generation consists of greeting sentences or states "cannot answer the question," always mark it as "yes."
2. For all other cases, provide a binary score: either "yes" or "no."
- Mark "yes" only if at least 80% retrieved facts align with the LLM generation and answer the question.
- All information in the LLM generation must be directly supported by the retrieved facts and answer the question. Do not consider any prior knowledge or external information.
3. If fewer than 80% facts align or if any part of the generation relies on unsupported information or give unrelated answer to the question, mark it as "no."
4. Do not call external APIs or use additional knowledge beyond the provided facts.
Be strict in your evaluation.
Give a detailed explanation of your decision as feedback.
Give a binary score 'yes' or 'no' score.
"""
response = client.beta.chat.completions.parse(
model=model,
messages=[
{"role": "system", "content": system_message},
{"role": "user", "content": f'Question:\n {question}, Set of facts:\n {document} Set of facts from previous conversation:\n {history_document}\n LLM generation:\n {generation}'}
],
response_format=GradeHallucinations
)
# score = response.choices[0].message.content
score = response.choices[0].message.parsed
print("HALLUCITANITON:", score)
# return GradeHallucinations(binary_score=score.strip())
# return score.binary_score, score.explanation, score.reproduced_answer
return score.binary_score, score.explanation
liked_state = gr.State(None)
last_interaction = gr.State(None)
def chat(question, manual, history, liked, graded_doc_history):
history = history or []
graded_doc_history = graded_doc_history or []
conv = format_chat_prompt(history)
# print("History: ", history)
# print("CONV: ", conv)
# print("Graded_doc_history: ", graded_doc_history)
manual_list = {"Toyota_Corolla_2024_TR": -8580416610875007536,
"Renault_Clio_2024_TR":-5514489544983735006,
"Fiat_Egea_2024_TR":-2026113796962100812}
collection_list = {"Toyota_Corolla_2024_TR": "HYBRID_TOYOTA_MANUAL_COLLECTION_EMBED3",
"Renault_Clio_2024_TR": "HYBRID_RENAULT_MANUAL_COLLECTION_EMBED3",
"Fiat_Egea_2024_TR": "HYBRID_FIAT_MANUAL_COLLECTION_EMBED3"}
collection_name = collection_list[manual]
toc_name = manual + "_TOC.txt"
start_time = time.time()
with open("ToCs/" + toc_name, "r") as file:
content = json.loads(file.read())
print("ToCs:--- %s seconds ---" % (time.time() - start_time))
# start_time = time.time()
# db = obj_loader.load_from_database(embeddings=embeddings, collection_name=collection_name)
# print("DB Load:--- %s seconds ---" % (time.time() - start_time))
start_time = time.time()
for i in range(2):
first_hop = f"""Soruyu cevaplarken:
1- Önce soruyu düşün.
2- Kullanıcının sorduğu sorunun konu başlıkları neler olabilir?
3- Sorulan soru bir arızaya işaret ediyor olabilir mi?
4- Bu konu başlıkları kullanım kılavuzu içindekiler tablosu başlıkları ile alakalı mı?
5- Alakalı olabilecek tüm başlıkları türet.
Buna göre, aşağıda vereceğim kullanım kılavuzu içindekiler tablosu (başlıklar) bilgisini kullanarak bu içeriğe erişmek için uygun fonksiyonları üret.
Eğer herhangi bir içeriğe ulaşamazsan, bunu belir ve sorunun cevabı hakkında yorum yapma.
Kullanım Kılavuzu İçindekiler Tablosu:
{content}
"""
# conv = [{"role": "system", "content": f"{first_hop}"}]
# conv.append({"role": "system", "content": f"{first_hop}"})
# first_hop_response = chat_gpt(prompt=f"Soru: {question}", history=conv, tools=retrieval_functions)
# conv.append(first_hop_response.choices[-1].message)
try:
first_hop_response = chat_gpt(prompt=f"Soru: {question}",
history=[{"role": "system", "content": f"{first_hop}"}],
tools=retrieval_functions)
tool_calls = first_hop_response.choices[-1].message.tool_calls
arg_list = []
if tool_calls:
for tool_call in tool_calls:
function_name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
arg_list.append(args)
print(f"Will call {function_name} with arguments {args}")
break
else:
print("No tool calls")
except openai.APIStatusError as e:
print(f"OpenAI API returned an API Error: {e}")
break
print("First_hop:--- %s seconds ---" % (time.time() - start_time))
path = "Contents/" + manual + ".json"
start_time = time.time()
documents = []
documents_pages = []
for i in arg_list:
context = get_section_content(str(i['section_title']), str(i['sub_section_title']), str(i['sub_sub_section_title']), content_path=path)
# documents_pages.append(documents[-1].metadata["page"])
if context:
# print("CONTEXT:", context.metadata["page"])
documents.append(context)
documents_pages.append(context.metadata["page"])
# break
# print("Documents:", documents)
CAR_ID = manual_list[manual]
path_PBP = "PBP-MANUALs/" + manual + ".json"
with open(path_PBP, "r") as file:
documents_page_by_page = json.loads(file.read())
documents_db = retriever_db(qclient, question, collection_name, CAR_ID)
print("Retriever:--- %s seconds ---" % (time.time() - start_time))
start_time = time.time()
document_page_nums = []
for i in documents_db:
# documents.append(i)
document_page_nums.append(i.metadata["page"])
# print("RETRIEVED DOCUMENTS:", i.metadata["page"])
# for i in np.unique(document_page_nums):
# documents.append(Document(metadata={"page": documents_page_by_page[i-1]["page_number"]},
# page_content = documents_page_by_page[i-1]["page_content"]) )
for i, p in enumerate(np.unique(document_page_nums).tolist()): # renault gibi manualler için 1.21vs ayrımını yapıyor
print("PBP_NUMBER:", p)
if isinstance(p, int):
flag = 0
for j in documents_pages:
if len(j.split(" - ")) > 1:
if p<=int(j.split(" - ")[-1]) and p>=int(j.split(" - ")[0]):
flag = 1
else:
if p == int(j):
flag = 1
if flag == 0:
try:
documents.append(Document(metadata={"page": documents_page_by_page[p-1]["page_number"]},
page_content = documents_page_by_page[p-1]["page_content"]) )
except: # DB güncellendikten sonra silinecek. DB'de son sayfa fazla konulmuş.
documents.append(Document(metadata={"page": documents_page_by_page[-1]["page_number"]},
page_content = documents_page_by_page[-1]["page_content"]))
else:
documents.append(documents_db[i])
print("Organizer:--- %s seconds ---" % (time.time() - start_time))
# print("Documents: ", documents)
start_time = time.time()
graded_doc = []
document_tokens = 0
for r in documents:
grade = grade_document_with_openai(r, question)
# print("BEFORE APPROVED:", r.metadata["page"])
# if grade.binary_score == "yes":
if grade == "yes":
document_tokens += len(encoding.encode(str(r)))
if document_tokens > 126000:
print("Maximum token limit exceeded.")
break
else:
graded_doc.append(r)
print("APPROVED:", r.metadata["page"])
# print("GRADED DOC:", graded_doc)
# print("GRADED DOC HISTORY", graded_doc_history)
# if not graded_doc:
# documents = []
# CAR_ID = manual_list[manual]
# documents_db = retriever_db(qclient, question, collection_name, CAR_ID)
# for i in documents_db:
# documents.append(i)
# for r in documents:
# grade = grade_document_with_openai(r, question)
# if grade.binary_score == "yes":
# graded_doc.append(r)
print("Onaylanan Doküman Sayısı:", len(graded_doc))
# print(graded_doc_history)
print("Grade Doc:--- %s seconds ---" % (time.time() - start_time))
marka = manual.split("_")[0]
model = manual.split("_")[1]
yıl = manual.split("_")[2]
start_time = time.time()
if get_lead_result(question).lower() == 'yes':
lead = f"""- Always convert "yetkili servis", "marka servisi", "yetkili bayi", "{marka} bayisi", "{marka} servisi" phrases with "profesyonel".
"""
print('Lead: yes')
else:
lead = f"""- Always convert "yetkili servis", "marka servisi", "yetkili bayi", "{marka} bayisi", "{marka} servisi" phrases with "profesyonel".
"""
print('Lead: no')
print("Lead Check:--- %s seconds ---" % (time.time() - start_time))
start_time = time.time()
lead_result = get_lead_result(question).lower()
prompt = f"""
You are an assistant named ARVI for a vehicle. Your task is to serve as a user manual consultant for the {yıl} {marka} {model} vehicle. You can only answer questions about the vehicle using the information provided in the user manual documents (within specified XML tags).
Respond appropriately and politely to basic courtesy interactions.
IMPORTANT: The documents have already been controlled for relevance to the question using an LLM-based grader. Therefore:
- Assume that all provided documents are relevant to the question.
- Focus on extracting and presenting information from these documents without questioning their relevance.
IMPORTANT: ONLY use the information provided in the documents (within XML tags). Do not make any comments or add information from external sources or prior knowledge. Your answers must be based solely on the information in the documents.
If no relevant information is found in the document:
- Before concluding that no information exists, ensure you have thoroughly checked all provided content.
- If no relevant data is indeed available, indicate that this information is not included in the user manual, and explain that you cannot answer the question based on available data. Do not make any additional comments.
When answering questions, follow these rules:
- Use only the information provided in the document when it is relevant to the question. Assume that all provided content is relevant unless proven otherwise.
- Do not provide personal opinions or use prior knowledge.
- Reproduce the information from the document exactly, without making any comments or adding explanations.
- Do not use terms or information outside of what is in the document.
- If there are warnings or dangers mentioned in the document, highlight them clearly.
- Keep your answers short and meaningful. Avoid unnecessary repetition.
- If a question relates to a previous one, evaluate both questions together.
- If more detail is requested by the user, provide as much detailed information as possible from available sources.
- If a question is too general, ask for more specific details.
{lead}
- Return the value below at the beginning of your response.
OtoBakım Lead: {lead_result}
When listing sources, follow these rules:
Return sources in Turkish.
1. Source Structure:
- Combine sources with identical heading structures under a single reference
(e.g., section_title -> sub_section_title -> sub_sub_section_title)
- If headings are empty or not specified in the document, skip those headings and only list what exists.
- List each unique heading structure only once.
2. Page Number Format:
- Use a hyphen (-) for consecutive pages (e.g., 1-4)
- Use a comma (,) for non-consecutive pages (e.g., 1, 3, 7)
- Remove repeating page numbers and combine all page numbers for each reference into a single "Page:" line.
3. Format Template:
Kaynaklar:
a. [section_title -> sub_section_title -> sub_sub_section_title] | Sayfa: X-Y, Z
b. [section_title] | Sayfa: A, B, D-F // If only main title exists
Final Check:
- Verify that your answer is accurate and appropriate to the question; ensure you adhered to the document.
- Ensure you did not share unnecessary information or make comments.
- Ensure references are correctly added.
{graded_doc_history}
{graded_doc}
You may now answer the question in Turkish.
"""
prompt_hallucination_response = f"""You are an assistant named ARVI for a vehicle. Your task is to serve as a user manual consultant for the {yıl} {marka} {model} vehicle.
In a manner consistent with the question, state that this information is not in the user manual, why you cannot answer the question, and do not comment on the question or make suggestions.
You may now answer in Turkish.
"""
for r in graded_doc:
graded_doc_history.append(r)
try:
conv.append({"role": "system", "content": f"{prompt}"})
final_response = chat_gpt_nofn(prompt=f"Question: {question}", history=conv).choices[-1].message.content
# final_response = chat_gpt_nofn(prompt=prompt, history=conv)
print("Answer-1:--- %s seconds ---" % (time.time() - start_time))
start_time = time.time()
# hallucination_check, _, new_answer = check_hallucinations(graded_doc, graded_doc_history, final_response, question, lead, lead_result)
hallucination_check, explanation = check_hallucinations(graded_doc, graded_doc_history, final_response, question)
print("Hallucination Score: ",hallucination_check)
print("Hallucination Check:--- %s seconds ---" % (time.time() - start_time))
if hallucination_check == "no":
print("ANSWER BEFORE HALLUCINATION:", final_response)
# final_response = new_answer
hallucination_correction = f"""\nFEEDBACK:\-----------\n{explanation}"""
conv.pop()
conv.append({"role": "system", "content": hallucination_correction})
start_time = time.time()
final_response = chat_gpt_nofn(prompt=f"Question: {question}", history=conv).choices[-1].message.content
print("Answer-1:--- %s seconds ---" % (time.time() - start_time))
except openai.APIStatusError as e:
final_response = f"OpenAI API returned an API Error: {e}"
# if hallucination_check.binary_score == "no":
# warning_prompt = prompt + "WARNING: YOU WERE HALLUCINATING. STICK TO THE FACTS in the DOCUMENTS. DO NOT MENTION ANYTHING OUTSIDE OF THE DOCUMENTS."
# print("Hallucination Score-1: ",hallucination_check.binary_score)
# conv.pop()
# conv.append({"role": "system", "content": f"{warning_prompt}"})
# final_response = chat_gpt_nofn(prompt=f"Question: {question}", history=conv)
# print(final_response.choices[-1].message.content)
# hallucination_check = check_hallucinations(graded_doc, final_response.choices[-1].message.content)
# if hallucination_check.binary_score == "no":
# print("Hallucination Score-2: ",hallucination_check.binary_score)
# conv.pop()
# conv.append({"role": "system", "content": f"{prompt_hallucination_response}"})
# final_response = chat_gpt_nofn(prompt=f"Question: {question}", history=conv)
partial_response = ""
for chunk in final_response.split(" "):
time.sleep(0.1)
try:
partial_response += chunk
partial_response += " "
yield partial_response, history + [(question, partial_response)], graded_doc_history
except:
pass
response = partial_response
# conv.append({"role": "user", "content": prompt})
conv.append({"role": "assistant", "content": response})
history.append((question, response))
# print("Answer:--- %s seconds ---" % (time.time() - start_time))
# Store the last interaction without saving to the database yet
last_interaction.value = {
"question": question,
"response": response,
"manual": manual,
"point_id": uuid.uuid4().hex
}
yield response, history, graded_doc_history
def save_last_interaction(feedback):
if last_interaction.value:
DatabaseOperations.save_user_history_demo(
qclient,
"USER_COLLECTION_EMBED_V3",
last_interaction.value["question"],
last_interaction.value["response"],
dense_embedding_model,
last_interaction.value["point_id"],
last_interaction.value["manual"],
feedback
)
last_interaction.value = None
manual_list = ["Toyota_Corolla_2024_TR", "Renault_Clio_2024_TR", "Fiat_Egea_2024_TR"]
with gr.Blocks() as demo:
chatbot = gr.Chatbot(height=600)
manual = gr.Dropdown(label="Kullanım Kılavuzları", value="Toyota_Corolla_2024_TR", choices=manual_list)
textbox = gr.Textbox()
graded_doc_history = gr.State([])
clear = gr.ClearButton(components=[textbox, chatbot, graded_doc_history], value='Clear console')
def handle_like(data: gr.LikeData):
liked_state.value = data.liked
if liked_state.value is not None:
feedback = "LIKE" if liked_state.value else "DISLIKE"
save_last_interaction(feedback)
# def gradio_chat(question, manual, history, graded_doc_history):
# save_last_interaction("N/A") # Save previous interaction before starting a new one
# chat_generator = chat(question, manual, history, liked_state.value, graded_doc_history)
# final_response = ""
# final_history = history
# for partial_response, updated_history, doc_history in chat_generator:
# final_response += partial_response
# final_history = updated_history
# yield "", final_history
# return "", final_history
def gradio_chat(question, manual, history, graded_doc_history):
save_last_interaction("N/A")
chat_generator = chat(question, manual, history, liked_state.value, graded_doc_history)
final_history = history
final_doc_history = graded_doc_history or []
for partial_response, updated_history, doc_history in chat_generator:
final_history = updated_history
final_doc_history = doc_history
yield "", final_history, final_doc_history
return "", final_history, final_doc_history
textbox.submit(gradio_chat,
[textbox, manual, chatbot, graded_doc_history],
[textbox, chatbot, graded_doc_history])
chatbot.like(handle_like, None, None)
demo.queue()
demo.launch()