DanielKiani's picture
Update scripts/main.py
52f8edc verified
# main.py
import torch
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.llms import LlamaCpp
from langchain.memory import ConversationBufferMemory
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.prompts import PromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate, ChatPromptTemplate
import os
import argparse # For command-line arguments
# Import the logic functions from src
import pipeline
# --- Global Objects & Setup ---
# (Similar setup as app.py, load models, prompts etc.)
print("--- Starting Local Execution Setup ---")
# 1. Check/Define Model Path
model_name = "mistral-7b-instruct-v0.1.Q4_K_M.gguf"
if not os.path.exists(model_name):
print(f"ERROR: Model file '{model_name}' not found. Please download it first.")
exit()
# 2. Prepare Default Sample Data (Optional, for context testing)
default_reviews_text = """...""" # Paste default laptop reviews
default_reviews_list = [r.strip() for r in default_reviews_text.strip().split('---') if r.strip()]
# 3. Load Embedding Model, Text Splitter
print("Loading embedding model and text splitter...")
model_kwargs = {'device': 'cuda' if torch.cuda.is_available() else 'cpu'}
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2", model_kwargs=model_kwargs)
text_splitter = RecursiveCharacterTextSplitter(chunk_size=250, chunk_overlap=40)
# 4. Create Default Vector Store
print("Creating default FAISS vector store...")
default_vector_store = pipeline.create_vector_store_from_content(
"\n---\n".join(default_reviews_list), text_splitter, embeddings
)
if default_vector_store is None: raise ValueError("Failed to create default vector store!")
# 5. Load the LLM
print("Loading LLM (Mistral-7B GGUF)...")
llm = LlamaCpp(
model_path=model_name, n_gpu_layers=0, n_batch=512, n_ctx=4096,
f16_kv=True, temperature=0.0, max_tokens=512, verbose=False,
stop=["[/INST]", "User:", "Assistant:"]
)
# 6. Define All Prompts
print("Defining all prompts...")
# -- Phase 1 --
summary_template = """[INST] ... Reviews:\n{reviews} [/INST]\nConcise Summary:"""
summary_prompt = PromptTemplate(template=summary_template, input_variables=["reviews"])
aspect_template = """[INST] ... Reviews:\n{reviews} [/INST]\nKey Pros and Cons:"""
aspect_prompt = PromptTemplate(template=aspect_template, input_variables=["reviews"])
sentiment_template = """[INST] ... Reviews:\n{reviews} [/INST]\nOverall Sentiment (Score 1-10):"""
sentiment_prompt = PromptTemplate(template=sentiment_template, input_variables=["reviews"])
# -- Phase 2 --
condense_question_template = """[INST] Given the following conversation... Follow Up Input: {question} [/INST]\nStandalone question:"""
CONDENSE_QUESTION_PROMPT = PromptTemplate.from_template(condense_question_template)
qa_system_prompt = """[INST]
You are a factual assistant that answers only using the provided product reviews.
If the reviews include partial or uncertain information, summarize what they say.
If there is no information at all about the user’s question, respond with:
"I'm sorry, there isn't enough information in the reviews to answer that."
Do not use or infer information about price, comparisons to other brands, or availability unless they are directly mentioned in the reviews.
Always include a short "Evidence:" sentence if you found relevant mentions.
Context:
{context}
User question:
{question}
[/INST]
"""
qa_prompt = ChatPromptTemplate.from_messages([SystemMessagePromptTemplate.from_template(qa_system_prompt), HumanMessagePromptTemplate.from_template("Context:\n{context}\n\nQuestion:\n{question}\n\nHelpful Answer:")])
intent_template = """
[INST]
**CRITICAL INSTRUCTION:** Classify the user's query into ONLY ONE of two categories: "Product" or "Off-Topic".
Your response MUST be EXACTLY "Product" or EXACTLY "Off-Topic".
**EXAMPLES:**
Query: How is the battery life?
Classification: Product
Query: What are the complaints about the screen?
Classification: Product
Query: Does it come in blue?
Classification: Product
Query: What is the capital of France?
Classification: Off-Topic
Query: Hello there
Classification: Off-Topic
Query: Who are you?
Classification: Off-Topic
**NOW CLASSIFY THIS QUERY:**
Query: {query}
[/INST]
Classification:"""
intent_prompt = PromptTemplate(template=intent_template, input_variables=["query"])
# 7. Memory Object (Needed for chatbot logic)
chat_memory = ConversationBufferMemory(memory_key='chat_history', return_messages=True, output_key='answer')
print("--- Local Setup Complete ---")
# --- Main Execution Logic ---
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Run ReviewSense Analysis or Chat locally.")
parser.add_argument("--mode", choices=['analyze', 'chat'], required=True, help="Mode to run: 'analyze' reviews from a file, or 'chat' interactively.")
parser.add_argument("--input", type=str, help="Path to input .txt file for 'analyze' mode, or initial query for 'chat' mode.")
parser.add_argument("--context", type=str, help="Optional: Path to a .txt file to use as context for 'chat' mode (defaults to built-in laptop reviews).")
args = parser.parse_args()
# --- ANALYZE MODE ---
if args.mode == 'analyze':
if not args.input or not os.path.exists(args.input):
print(f"Error: Input file '{args.input}' not found for analyze mode.")
exit()
print(f"\n--- Running Analysis on: {args.input} ---")
try:
with open(args.input, 'r', encoding='utf-8') as f:
review_content = f.read()
except Exception as e:
print(f"Error reading input file: {e}")
exit()
summary, aspects, sentiment = pipeline.analyze_reviews_logic(
review_content, llm, summary_prompt, aspect_prompt, sentiment_prompt
)
print("\n--- Analysis Results ---")
print("\n[Summary]")
print(summary)
print("\n[Aspects]")
print(aspects)
print("\n[Sentiment]")
print(sentiment)
# --- CHAT MODE ---
elif args.mode == 'chat':
print("\n--- Starting Interactive Chat ---")
# Determine context
chat_vector_store = default_vector_store
context_name = "Default Laptop Reviews"
if args.context:
if not os.path.exists(args.context):
print(f"Warning: Context file '{args.context}' not found. Using default context.")
else:
print(f"Loading context from: {args.context}")
try:
with open(args.context, 'r', encoding='utf-8') as f:
context_content = f.read()
chat_vector_store = pipeline.create_vector_store_from_content(
context_content, text_splitter, embeddings
)
if chat_vector_store:
context_name = f"File: {os.path.basename(args.context)}"
else:
print("Failed to load context file. Using default context.")
chat_vector_store = default_vector_store
except Exception as e:
print(f"Error reading context file '{args.context}': {e}. Using default context.")
chat_vector_store = default_vector_store
print(f"Using context: {context_name}")
chat_memory.clear() # Start fresh chat session
# Handle initial query if provided
if args.input:
print("\nUser:", args.input)
response = pipeline.get_chatbot_response(
message=args.input,
chat_memory=chat_memory,
vector_store=chat_vector_store,
llm=llm,
intent_prompt=intent_prompt,
condense_prompt=CONDENSE_QUESTION_PROMPT,
qa_prompt=qa_prompt
)
print("\nAssistant:", response)
# Interactive loop
print("\nEnter your questions (type 'quit' or 'exit' to stop):")
while True:
try:
user_message = input("\nUser: ")
if user_message.lower() in ['quit', 'exit']:
break
if not user_message:
continue
response = pipeline.get_chatbot_response(
message=user_message,
chat_memory=chat_memory,
vector_store=chat_vector_store,
llm=llm,
intent_prompt=intent_prompt,
condense_prompt=CONDENSE_QUESTION_PROMPT,
qa_prompt=qa_prompt
)
print("\nAssistant:", response)
except EOFError: # Handle Ctrl+D
break
except KeyboardInterrupt: # Handle Ctrl+C
break
print("\n--- Chat session ended. ---")
print("\n--- Local Execution Finished ---")