File size: 9,026 Bytes
64fbadd
7c4f775
64fbadd
 
 
 
 
 
 
 
 
 
52f8edc
64fbadd
 
 
 
 
 
 
 
7c4f775
 
64fbadd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7c4f775
64fbadd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e580bee
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# 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 ---")