Spaces:
Sleeping
Sleeping
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: | |
<b>Kaynaklar:</b> | |
<b>a.</b> [section_title -> sub_section_title -> sub_sub_section_title] <b>|</b> Sayfa: X-Y, Z | |
<b>b.</b> [section_title] <b>|</b> 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. | |
<document history> {graded_doc_history} </document history> | |
<document> {graded_doc} </document> | |
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() | |