Commit 7d8d6f87 authored by Mirza Mohammed Baig's avatar Mirza Mohammed Baig

Chat bot Nissi AI Implemented

parent cedf25ae
...@@ -1053,14 +1053,14 @@ tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<10" ...@@ -1053,14 +1053,14 @@ tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<10"
[[package]] [[package]]
name = "langchain-core" name = "langchain-core"
version = "0.3.37" version = "0.3.45"
description = "Building applications with LLMs through composability" description = "Building applications with LLMs through composability"
optional = false optional = false
python-versions = "<4.0,>=3.9" python-versions = "<4.0,>=3.9"
groups = ["main"] groups = ["main"]
files = [ files = [
{file = "langchain_core-0.3.37-py3-none-any.whl", hash = "sha256:8202fd6506ce139a3a1b1c4c3006216b1c7fffa40bdd1779f7d2c67f75eb5f79"}, {file = "langchain_core-0.3.45-py3-none-any.whl", hash = "sha256:fe560d644c102c3f5dcfb44eb5295e26d22deab259fdd084f6b1b55a0350b77c"},
{file = "langchain_core-0.3.37.tar.gz", hash = "sha256:cda8786e616caa2f68f7cc9e811b9b50e3b63fb2094333318b348e5961a7ea01"}, {file = "langchain_core-0.3.45.tar.gz", hash = "sha256:a39b8446495d1ea97311aa726478c0a13ef1d77cb7644350bad6d9d3c0141a0c"},
] ]
[package.dependencies] [package.dependencies]
......
from functools import lru_cache
from pydantic.v1 import BaseSettings
class Settings(BaseSettings):
"""Application configuration settings"""
ASTRA_DB_API_ENDPOINT: str = "https://0c52512b-0c65-4e70-ac47-71edf5244a82-us-east-2.apps.astra.datastax.com"
ASTRA_DB_APPLICATION_TOKEN: str = "AstraCS:EvXpFFafufegdQJvhqlYxmxt:ef86b5996013b12140b69254bd554d7e8e10eb5a7137859b9c432f92a5a3b65c"
ASTRA_DB_NAMESPACE: str = "default_keyspace"
# Default settings
EMBEDDING_MODEL: str = "sentence-transformers/all-mpnet-base-v2"
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
extra = "allow" # Allow extra fields
@lru_cache()
def get_settings():
"""
Cached settings retrieval to optimize performance
Returns:
Settings: Configured application settings
"""
return Settings()
from langchain_groq import ChatGroq from langchain_groq import ChatGroq
from functools import lru_cache from functools import lru_cache
from langchain_core.messages import BaseMessage
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():
""" """
Get a singleton LLM instance for the given Groq token. Get a singleton LLM instance for the given Groq token.
...@@ -13,8 +15,85 @@ def get_llm(groq_token: str): ...@@ -13,8 +15,85 @@ 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."
from groq import BaseModel from pydantic import BaseModel
class PercentageResponseModel(BaseModel): class PercentageResponseModel(BaseModel):
positive_percentage: str positive_percentage: str
negative_percentage: str negative_percentage: str
......
from pydantic import BaseModel, Field
from typing import List, Optional
class UserMessage(BaseModel):
message: str = "suggest me a mobile with okay battery backup"
\ No newline at end of file
...@@ -12,6 +12,8 @@ from src.reviewsense_ecom.service.ReviewService import ReviewService ...@@ -12,6 +12,8 @@ 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()
...@@ -124,3 +126,20 @@ async def update_product_data(input_data, product_data): ...@@ -124,3 +126,20 @@ async def update_product_data(input_data, product_data):
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.service.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."
...@@ -19,7 +19,7 @@ class FeatureExtractor: ...@@ -19,7 +19,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(api_key) self.llm = get_llm()
self.parser = self._create_reviews_parser() self.parser = self._create_reviews_parser()
self.prompt = self._create_extraction_prompt() self.prompt = self._create_extraction_prompt()
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
from functools import lru_cache from functools import lru_cache
from langchain_huggingface import HuggingFaceEmbeddings from langchain_huggingface import HuggingFaceEmbeddings
from langchain_astradb import AstraDBVectorStore from langchain_astradb import AstraDBVectorStore
from src.reviewsense_ecom.core.config import get_settings
@lru_cache(maxsize=1) @lru_cache(maxsize=1)
...@@ -19,6 +21,7 @@ def get_vector_store( ...@@ -19,6 +21,7 @@ def get_vector_store(
Returns: Returns:
AstraDBVectorStore: Configured vector store instance AstraDBVectorStore: Configured vector store instance
""" """
settings = get_settings()
# Initialize embeddings # Initialize embeddings
embeddings = HuggingFaceEmbeddings(model_name=embedding_model) embeddings = HuggingFaceEmbeddings(model_name=embedding_model)
......
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