File size: 8,127 Bytes
0a81317 e764d84 0a81317 033375f 0a81317 033375f 0a81317 033375f 0a81317 033375f 0a81317 8907b38 033375f 0a81317 033375f 0a81317 033375f 0a81317 e367093 0a81317 033375f 0a81317 033375f 0a81317 033375f 0a81317 033375f 0a81317 033375f 0a81317 033375f 0a81317 033375f 0a81317 033375f 2a28b9c 033375f 0a81317 033375f e764d84 033375f 0a81317 033375f 0a81317 033375f 0a81317 e764d84 033375f 0a81317 033375f 0a81317 033375f 0a81317 033375f 8907b38 033375f e764d84 033375f 0a81317 e764d84 033375f e764d84 033375f 0a81317 e764d84 033375f 0a81317 428a54e 0a81317 428a54e 0a81317 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
import os
import logging
import gradio as gr
from transformers import pipeline
from sentence_transformers import SentenceTransformer, util
import PyPDF2
# Set up logging with immediate writing
logging.basicConfig(
filename='support_bot_log.txt',
level=logging.INFO,
format='%(asctime)s - %(message)s',
force=True # Ensures any existing handlers are replaced and logging starts fresh
)
logger = logging.getLogger()
# Load models
qa_model = pipeline("question-answering", model="distilbert-base-uncased-distilled-squad")
embedder = SentenceTransformer('all-MiniLM-L6-v2')
# Helper function to extract text from PDF
def extract_text_from_pdf(file_path):
text = ""
with open(file_path, "rb") as file:
pdf_reader = PyPDF2.PdfReader(file)
for page in pdf_reader.pages:
text += page.extract_text() + "\n"
return text
# Find the most relevant section in the document
def find_relevant_section(query, sections, section_embeddings):
stopwords = {"and", "the", "is", "for", "to", "a", "an", "of", "in", "on", "at", "with", "by", "it", "as", "so", "what"}
# Semantic search
query_embedding = embedder.encode(query, convert_to_tensor=True)
similarities = util.cos_sim(query_embedding, section_embeddings)[0]
best_idx = similarities.argmax().item()
best_section = sections[best_idx]
similarity_score = similarities[best_idx].item()
SIMILARITY_THRESHOLD = 0.4
if similarity_score >= SIMILARITY_THRESHOLD:
logger.info(f"Found relevant section using embeddings for query: {query}")
return best_section
logger.info(f"Low similarity ({similarity_score}). Falling back to keyword search.")
# Keyword-based fallback search with stopword filtering
query_words = {word for word in query.lower().split() if word not in stopwords}
for section in sections:
section_words = {word for word in section.lower().split() if word not in stopwords}
common_words = query_words.intersection(section_words)
if len(common_words) >= 2:
logger.info(f"Keyword match found for query: {query} with common words: {common_words}")
return section
logger.info(f"No good keyword match found. Returning default fallback response.")
return "I don’t have enough information to answer that."
# Process the uploaded file with detailed logging
def process_file(file, state):
if file is None:
logger.info("No file uploaded.")
return [("Bot", "Please upload a file.")], state
file_path = file.name
if file_path.lower().endswith(".pdf"):
logger.info(f"Uploaded PDF file: {file_path}")
text = extract_text_from_pdf(file_path)
elif file_path.lower().endswith(".txt"):
logger.info(f"Uploaded TXT file: {file_path}")
with open(file_path, 'r', encoding='utf-8') as f:
text = f.read()
else:
logger.error(f"Unsupported file format: {file_path}")
return [("Bot", "Unsupported file format. Please upload a PDF or TXT file.")], state
sections = text.split('\n\n')
section_embeddings = embedder.encode(sections, convert_to_tensor=True)
state['document_text'] = text
state['sections'] = sections
state['section_embeddings'] = section_embeddings
state['current_query'] = None
state['feedback_count'] = 0
state['mode'] = 'waiting_for_query'
state['chat_history'] = [("Bot", "File processed. You can now ask questions.")]
logger.info(f"Processed file: {file_path}")
return state['chat_history'], state
# Handle user input (queries and feedback)
def handle_input(user_input, state):
if state['mode'] == 'waiting_for_upload':
state['chat_history'].append(("Bot", "Please upload a file first."))
logger.info("User attempted to interact without uploading a file.")
elif state['mode'] == 'waiting_for_query':
query = user_input
state['current_query'] = query
state['feedback_count'] = 0
context = find_relevant_section(query, state['sections'], state['section_embeddings'])
if context == "I don’t have enough information to answer that.":
answer = context
else:
result = qa_model(question=query, context=context)
answer = result["answer"]
state['last_answer'] = answer
state['mode'] = 'waiting_for_feedback'
state['chat_history'].append(("User", query))
state['chat_history'].append(("Bot", f"Answer: {answer}\nPlease provide feedback: good, too vague, not helpful."))
logger.info(f"Query: {query}, Answer: {answer}")
elif state['mode'] == 'waiting_for_feedback':
feedback = user_input.lower()
state['chat_history'].append(("User", feedback))
logger.info(f"Feedback: {feedback}")
if feedback == "good" or state['feedback_count'] >= 2:
state['mode'] = 'waiting_for_query'
if feedback == "good":
state['chat_history'].append(("Bot", "Thank you for your feedback. You can ask another question."))
logger.info("Feedback accepted as 'good'. Waiting for next query.")
else:
state['chat_history'].append(("Bot", "Maximum feedback iterations reached. You can ask another question."))
logger.info("Max feedback iterations reached. Waiting for next query.")
else:
query = state['current_query']
context = find_relevant_section(query, state['sections'], state['section_embeddings'])
if feedback == "too vague":
adjusted_answer = f"{state['last_answer']}\n\n(More details:\n{context[:500]}...)"
elif feedback == "not helpful":
adjusted_answer = qa_model(question=query + " Please provide more detailed information with examples.", context=context)['answer']
else:
state['chat_history'].append(("Bot", "Please provide valid feedback: good, too vague, not helpful."))
logger.info(f"Invalid feedback received: {feedback}")
return state['chat_history'], state
state['last_answer'] = adjusted_answer
state['feedback_count'] += 1
state['chat_history'].append(("Bot", f"Updated answer: {adjusted_answer}\nPlease provide feedback: good, too vague, not helpful."))
logger.info(f"Adjusted answer: {adjusted_answer}")
return state['chat_history'], state
# Function to return the up-to-date log file for download
def get_log_file():
# Flush all log handlers to ensure log file is current
for handler in logger.handlers:
handler.flush()
# Ensure the log file exists; if not, create an empty one.
if not os.path.exists("support_bot_log.txt"):
with open("support_bot_log.txt", "w", encoding="utf-8") as f:
f.write("")
logger.info("Log file downloaded by user.")
return "support_bot_log.txt"
# Initial state
initial_state = {
'document_text': None,
'sections': None,
'section_embeddings': None,
'current_query': None,
'feedback_count': 0,
'mode': 'waiting_for_upload',
'chat_history': [("Bot", "Please upload a PDF or TXT file to start.")],
'last_answer': None
}
# Gradio interface
with gr.Blocks() as demo:
state = gr.State(initial_state)
with gr.Row():
file_upload = gr.File(label="Upload PDF or TXT file")
download_btn = gr.Button("Download Log")
download_file = gr.File(label="Log File", interactive=False)
chat = gr.Chatbot()
user_input = gr.Textbox(label="Your query or feedback")
submit_btn = gr.Button("Submit")
# Process file upload
file_upload.upload(process_file, inputs=[file_upload, state], outputs=[chat, state])
# Handle user input and clear the textbox
submit_btn.click(handle_input, inputs=[user_input, state], outputs=[chat, state]).then(lambda: "", None, user_input)
# Set up download log button
download_btn.click(fn=get_log_file, inputs=[], outputs=download_file)
demo.launch(share=True)
|