Commit c05bf261 authored by Bhargava Rellu's avatar Bhargava Rellu

remove the chatbot code

parent b1dc058d
...@@ -2,5 +2,5 @@ ASTRA_DB_API_ENDPOINT="https://0c52512b-0c65-4e70-ac47-71edf5244a82-us-east-2.ap ...@@ -2,5 +2,5 @@ ASTRA_DB_API_ENDPOINT="https://0c52512b-0c65-4e70-ac47-71edf5244a82-us-east-2.ap
ASTRA_DB_APPLICATION_TOKEN="AstraCS:EvXpFFafufegdQJvhqlYxmxt:ef86b5996013b12140b69254bd554d7e8e10eb5a7137859b9c432f92a5a3b65c" ASTRA_DB_APPLICATION_TOKEN="AstraCS:EvXpFFafufegdQJvhqlYxmxt:ef86b5996013b12140b69254bd554d7e8e10eb5a7137859b9c432f92a5a3b65c"
ASTRA_DB_NAMESPACE="default_keyspace" ASTRA_DB_NAMESPACE="default_keyspace"
HF_TOKEN="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" HF_TOKEN="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"
GROQ_API_KEY="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" GROQ_API_KEY="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"
MONGODB_URL=mongodb+srv://genai:nisum@123@productrater.hpbsn.mongodb.net/?retryWrites=true&w=majority&appName=productRater MONGODB_URL=mongodb+srv://genai:nisum@123@productrater.hpbsn.mongodb.net/?retryWrites=true&w=majority&appName=productRater
...@@ -4,6 +4,7 @@ from functools import lru_cache ...@@ -4,6 +4,7 @@ from functools import lru_cache
from langchain_core.messages import BaseMessage from langchain_core.messages import BaseMessage
from langchain_core.prompts import ChatPromptTemplate from langchain_core.prompts import ChatPromptTemplate
@lru_cache(maxsize=1) # Cache only one instance (singleton behavior) @lru_cache(maxsize=1) # Cache only one instance (singleton behavior)
def get_llm(groq_token: str): def get_llm(groq_token: str):
""" """
...@@ -15,85 +16,8 @@ def get_llm(groq_token: str): ...@@ -15,85 +16,8 @@ def get_llm(groq_token: str):
Returns: Returns:
ChatGroq: A singleton instance of the ChatGroq LLM. ChatGroq: A singleton instance of the ChatGroq LLM.
""" """
groq_token="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"
return ChatGroq( return ChatGroq(
model_name="llama-3.3-70b-versatile", model_name="llama-3.3-70b-versatile",
temperature=1, temperature=1,
groq_api_key=groq_token # Pass the Groq token here groq_api_key=groq_token # Pass the Groq token here
) )
def generate_response(
query: str,
context: str,
max_context_length: int = 2000
) -> str:
"""
Generate response using LLM for ecommerce product queries.
Args:
query: User query string
context: Context string containing product details and reviews
max_context_length: Maximum allowed length for context
Returns:
Generated response string
Raises:
ValueError: If input parameters are invalid
Exception: For other processing errors
"""
# Input validation
if not query or not query.strip():
raise ValueError("Query cannot be empty")
if not context or not context.strip():
raise ValueError("Context cannot be empty")
try:
# Preprocess context
context = context.strip()
if len(context) > max_context_length:
context = context[:max_context_length] + "..."
llm_template = """
Your task is to act as an ecommerce product expert assistant.
Analyze the provided product information and reviews carefully.
Guidelines:
- Provide accurate product-specific information based on the context
- Keep responses concise and focused on the query
- Include relevant product features and customer feedback
- If information is not in the context, acknowledge the limitation
- Maintain a helpful and professional tone
CONTEXT:
{context}
QUESTION: {input}
YOUR ANSWER:
"""
# Create and execute the chain
chain = ChatPromptTemplate.from_template(llm_template) | get_llm()
result = chain.invoke({
"context": context,
"input": query
})
# Extract response text based on message type
if isinstance(result, BaseMessage):
response = result.content
elif isinstance(result, str):
response = result
else:
response = str(result)
# Validate response
if not response or not response.strip():
raise ValueError("Empty response received from LLM")
return response.strip()
except Exception as e:
return "I apologize, but I encountered an error while processing your request. Please try again or contact support if the issue persists."
import logging import logging
from fastapi import APIRouter, HTTPException from fastapi import APIRouter, HTTPException
from src.reviewsense_ecom.model.PercentageResponseModel import PercentageResponseModel from src.reviewsense_ecom.model.PercentageResponseModel import PercentageResponseModel
from src.reviewsense_ecom.model.ReviewResponseModel import ReviewResponseModel from src.reviewsense_ecom.model.ReviewResponseModel import ReviewResponseModel
from src.reviewsense_ecom.service.FeatureUpdater import FeatureUpdater from src.reviewsense_ecom.service.FeatureUpdater import FeatureUpdater
from src.reviewsense_ecom.model.Product import Product from src.reviewsense_ecom.model.Product import Product
from src.reviewsense_ecom.service.ReviewService import ReviewService from src.reviewsense_ecom.service.ReviewService import ReviewService
from src.reviewsense_ecom.service.feature_extractor import FeatureExtractor from src.reviewsense_ecom.service.feature_extractor import FeatureExtractor
from src.reviewsense_ecom.mongo.mongo_db_config import get_product_by_id, update_product, add_review_features, insert_product,product_feature from src.reviewsense_ecom.mongo.mongo_db_config import get_product_by_id, update_product, add_review_features, \
insert_product, product_feature
from src.reviewsense_ecom.model.product_review_input import ProductReviewInput from src.reviewsense_ecom.model.product_review_input import ProductReviewInput
from src.reviewsense_ecom.model.UserMessage import UserMessage
from src.reviewsense_ecom.service.BotService import BotService
router = APIRouter() router = APIRouter()
review_service = ReviewService() review_service = ReviewService()
...@@ -21,6 +18,7 @@ feature_extractor = FeatureExtractor() ...@@ -21,6 +18,7 @@ feature_extractor = FeatureExtractor()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@router.post("/calculate_ratings/", response_model=Product) @router.post("/calculate_ratings/", response_model=Product)
async def calculate_ratings(input_data: ProductReviewInput): # Required async def calculate_ratings(input_data: ProductReviewInput): # Required
""" """
...@@ -56,14 +54,15 @@ async def calculate_ratings(input_data: ProductReviewInput): # Required ...@@ -56,14 +54,15 @@ async def calculate_ratings(input_data: ProductReviewInput): # Required
logger.info(f"Product Data: {product_data}") logger.info(f"Product Data: {product_data}")
final_product = product_data final_product = product_data
if input_data.is_rating_evaluation_required : if input_data.is_rating_evaluation_required:
final_product = await update_product_data( input_data, product_data) final_product = await update_product_data(input_data, product_data)
logger.info(f"Final Return to Mongo: {final_product}") logger.info(f"Final Return to Mongo: {final_product}")
return final_product return final_product
@router.get("/fetch_percentage/", response_model=PercentageResponseModel) @router.get("/fetch_percentage/", response_model=PercentageResponseModel)
async def fetch_percentage(product_id: str, features: str): #NOT REQUIRED async def fetch_percentage(product_id: str, features: str): # NOT REQUIRED
""" """
Fetch reviews from MongoDB based on product_id and feature. Fetch reviews from MongoDB based on product_id and feature.
""" """
...@@ -74,8 +73,9 @@ async def fetch_percentage(product_id: str, features: str): #NOT REQUIRED ...@@ -74,8 +73,9 @@ async def fetch_percentage(product_id: str, features: str): #NOT REQUIRED
return {"message": "No data found"} return {"message": "No data found"}
@router.get("/fetch_reviews/", response_model=ReviewResponseModel) @router.get("/fetch_reviews/", response_model=ReviewResponseModel)
async def fetch_review_by_feature(product_id: str, features: str): #NotRequired async def fetch_review_by_feature(product_id: str, features: str): # NotRequired
""" """
Fetch reviews from MongoDB based on product_id and feature. Fetch reviews from MongoDB based on product_id and feature.
""" """
...@@ -85,8 +85,10 @@ async def fetch_review_by_feature(product_id: str, features: str): #NotRequired ...@@ -85,8 +85,10 @@ async def fetch_review_by_feature(product_id: str, features: str): #NotRequired
return reviews return reviews
return {"reviews": reviews} return {"reviews": reviews}
@router.get("/fetch_full_reviews/", response_model=ReviewResponseModel) @router.get("/fetch_full_reviews/", response_model=ReviewResponseModel)
async def fetch_review_by_feature(product_id: str, features: str): #Required async def fetch_review_by_feature(product_id: str, features: str): # Required
""" """
Fetch reviews from MongoDB based on product_id and feature. Fetch reviews from MongoDB based on product_id and feature.
""" """
...@@ -97,13 +99,13 @@ async def fetch_review_by_feature(product_id: str, features: str): #Required ...@@ -97,13 +99,13 @@ async def fetch_review_by_feature(product_id: str, features: str): #Required
return {"reviews": reviews} return {"reviews": reviews}
async def update_product_data(input_data, product_data): async def update_product_data(input_data, product_data):
# ✅ Second Flow: Update Feature Ratings in Another Collection FROM LLM # ✅ Second Flow: Update Feature Ratings in Another Collection FROM LLM
features=product_feature(input_data.product_id) features = product_feature(input_data.product_id)
reviews_by_feature = feature_extractor.extract_feature_reviews( reviews_by_feature = feature_extractor.extract_feature_reviews(
input_data.new_review, features) input_data.new_review, features)
ratings = review_service.fetch_feature_ratings(reviews_by_feature) ratings = review_service.fetch_feature_ratings(reviews_by_feature)
logger.info(f"Generated ratings: {ratings}") logger.info(f"Generated ratings: {ratings}")
# ✅ First Flow: Add Review to a Separate Collection TO REVIEWS # ✅ First Flow: Add Review to a Separate Collection TO REVIEWS
...@@ -125,21 +127,3 @@ async def update_product_data(input_data, product_data): ...@@ -125,21 +127,3 @@ async def update_product_data(input_data, product_data):
logger.error("update_product() returned None. Update failed.") logger.error("update_product() returned None. Update failed.")
raise HTTPException(status_code=500, detail="Failed to update product") raise HTTPException(status_code=500, detail="Failed to update product")
return final_product return final_product
#ChatBotAPI
@router.post("/chat")
async def chat(user_message: UserMessage):
"""
Endpoint to handle user messages and return chatbot responses.
"""
if not user_message.message:
raise HTTPException(status_code=400, detail="Message cannot be empty")
bot_service = BotService()
# Handle a query
response = bot_service.handle_user_query(user_message.message)
# Or use individual components
return {"response": response}
from dataclasses import dataclass
from typing import List, Dict, Optional, Tuple, Any
from langchain_astradb import AstraDBVectorStore
from logging import getLogger
from functools import lru_cache
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import ChatPromptTemplate
from src.reviewsense_ecom.llm.llm import get_llm, generate_response
from src.reviewsense_ecom.retriever.retrieval import get_vector_store
logger = getLogger(__name__)
@dataclass
class ProductContext:
product_name: str
product_reviews: str
class BotServiceError(Exception):
"""Base exception for bot service errors"""
pass
class VectorStoreError(BotServiceError):
"""Exception for vector store related errors"""
pass
class ReviewProcessor:
@staticmethod
def clean_review(review: str) -> str:
"""Clean and format a single review"""
return review.strip() + ('.' if not review.strip().endswith('.') else '')
@staticmethod
def process_reviews(doc_content: str) -> List[str]:
"""Process document content into cleaned review sentences"""
return [
ReviewProcessor.clean_review(sentence)
for sentence in doc_content.split('.')
if sentence.strip()
]
class BotService:
def __init__(self, vector_store: Optional[AstraDBVectorStore] = None):
self.vector_store = vector_store or self._initialize_vector_store()
self.llm = get_llm()
@staticmethod
@lru_cache(maxsize=1)
def _initialize_vector_store() -> AstraDBVectorStore:
"""Initialize and cache vector store connection"""
try:
return get_vector_store()
except Exception as e:
logger.error(f"Failed to initialize vector store: {e}")
raise VectorStoreError(f"Vector store initialization failed: {e}")
def get_product_reviews(
self,
product_id: str,
query: str,
k: int = 1
) -> str:
"""
Retrieve and combine product reviews from the vector store.
Args:
product_id: Product identifier
query: Search query
k: Number of results to retrieve
Returns:
Combined review string
"""
try:
results = self.vector_store.similarity_search_with_score_id(
query=query,
k=k,
filter={"product_name": product_id}
)
all_reviews = []
for doc, _, _ in results:
reviews = ReviewProcessor.process_reviews(doc.page_content)
all_reviews.extend(reviews)
return ' '.join(all_reviews)
except Exception as e:
logger.error(f"Error retrieving product reviews: {e}")
raise VectorStoreError(f"Failed to retrieve reviews: {e}")
def retrieve_context(
self,
query: str,
search_query: str,
k: int = 1
) -> Optional[ProductContext]:
"""
Retrieve context from vector store.
Args:
query: User query
search_query: Search query for vector store
k: Number of results to retrieve
Returns:
ProductContext if found, None otherwise
"""
try:
as_retriever = self.vector_store.as_retriever(search_type="similarity_score_threshold",
search_kwargs={"k": 1, "score_threshold": 0.8}, )
results = as_retriever.invoke(search_query)
if not results:
logger.info("No results found for query")
return None
document = results[0]
product_name = document.metadata.get('product_name')
if not product_name:
logger.warning("Product name missing in document metadata")
return None
return ProductContext(
product_name=product_name,
product_reviews=self.get_product_reviews(query=query, product_id=product_name)
)
except Exception as e:
logger.error(f"Error retrieving context: {e}")
raise VectorStoreError(f"Context retrieval failed: {e}")
def handle_user_query(self, query: str) -> str:
"""
Process user query end-to-end.
Args:
query: User query
Returns:
Generated response
"""
try:
context = self.retrieve_context(query=query, search_query=query)
if not context:
return "I'm sorry, I couldn't find relevant information."
formatted_context = f"""Product Information:
Name: {context.product_name}
Customer Reviews:
{context.product_reviews}"""
return generate_response(
query=query,
context=formatted_context
)
except BotServiceError as e:
logger.error(f"Error handling user query: {e}")
return "I apologize, but I encountered an error processing your request."
...@@ -20,7 +20,7 @@ class FeatureExtractor: ...@@ -20,7 +20,7 @@ class FeatureExtractor:
api_key = os.getenv('GROQ_API_KEY') api_key = os.getenv('GROQ_API_KEY')
if not api_key: if not api_key:
raise ValueError("GROQ_API_KEY is not set in the environment variables.") raise ValueError("GROQ_API_KEY is not set in the environment variables.")
self.llm = get_llm() self.llm = get_llm(api_key)
self.parser = self._create_reviews_parser() self.parser = self._create_reviews_parser()
self.prompt = self._create_extraction_prompt() self.prompt = self._create_extraction_prompt()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment