File size: 8,060 Bytes
e764d84 03000c3 e764d84 03000c3 e764d84 03000c3 e764d84 acc276b e764d84 03000c3 e764d84 03000c3 e764d84 03000c3 e764d84 03000c3 e764d84 03000c3 e764d84 03000c3 e764d84 03000c3 e764d84 acc276b 03000c3 e764d84 03000c3 e764d84 03000c3 e764d84 03000c3 e764d84 03000c3 e764d84 03000c3 e764d84 03000c3 e764d84 03000c3 e764d84 03000c3 e764d84 03000c3 e764d84 03000c3 e764d84 acc276b 03000c3 e764d84 03000c3 e764d84 03000c3 e764d84 efcdc30 |
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 |
import logging
import gradio as gr
from transformers import pipeline
from sentence_transformers import SentenceTransformer, util
import PyPDF2
# Set up logging with a dedicated file handler
logger = logging.getLogger('SupportBot')
logger.setLevel(logging.INFO)
# Remove any existing handlers to avoid conflicts
if logger.handlers:
logger.handlers.clear()
# Create a file handler with append mode
handler = logging.FileHandler('support_bot_log.txt', mode='a')
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
# 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"}
logger.info(f"Searching for relevant section for query: {query}")
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 (score: {similarity_score})")
handler.flush() # Ensure log is written immediately
return best_section
logger.info(f"Low similarity ({similarity_score}). Falling back to keyword search.")
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 with common words: {common_words}")
handler.flush()
return section
logger.info("No good match found. Returning default response.")
handler.flush()
return "I don’t have enough information to answer that."
# Process the uploaded file
def process_file(file, state):
logger.info("Received file upload request")
if file is None:
logger.info("No file uploaded")
handler.flush()
return [("Bot", "Please upload a file.")], state
file_path = file.name
if file_path.lower().endswith(".pdf"):
logger.info(f"Processing PDF file: {file_path}")
text = extract_text_from_pdf(file_path)
elif file_path.lower().endswith(".txt"):
logger.info(f"Processing 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}")
handler.flush()
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"File processed successfully: {file_path}")
handler.flush()
return state['chat_history'], state
# Handle user input (queries and feedback)
def handle_input(user_input, state):
if state['mode'] == 'waiting_for_upload':
logger.info("User input received before file upload")
state['chat_history'].append(("Bot", "Please upload a file first."))
handler.flush()
elif state['mode'] == 'waiting_for_query':
query = user_input
logger.info(f"User query: {query}")
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"Generated answer: {answer}")
handler.flush()
elif state['mode'] == 'waiting_for_feedback':
feedback = user_input.lower()
logger.info(f"User feedback: {feedback}")
state['chat_history'].append(("User", 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 'good' received. Ready for next query.")
else:
state['chat_history'].append(("Bot", "Maximum feedback iterations reached. You can ask another question."))
logger.info("Max feedback iterations (2) reached. Ready for next query.")
handler.flush()
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]}...)"
logger.info("Feedback 'too vague'. Providing context.")
elif feedback == "not helpful":
adjusted_answer = qa_model(question=query + " Please provide more detailed information with examples.", context=context)['answer']
logger.info("Feedback 'not helpful'. Re-searching with modified query.")
else:
state['chat_history'].append(("Bot", "Please provide valid feedback: good, too vague, not helpful."))
logger.info(f"Invalid feedback received: {feedback}")
handler.flush()
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"Updated answer: {adjusted_answer}")
handler.flush()
return state['chat_history'], state
# 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)
file_upload = gr.File(label="Upload PDF or TXT file")
chat = gr.Chatbot()
user_input = gr.Textbox(label="Your query or feedback")
submit_btn = gr.Button("Submit")
log_file = gr.File(label="Download Log File", value="support_bot_log.txt")
file_upload.upload(process_file, inputs=[file_upload, state], outputs=[chat, state])
submit_btn.click(handle_input, inputs=[user_input, state], outputs=[chat, state]).then(lambda: "", None, user_input)
demo.launch(share=True) |