book / app.py
Arshdeep004's picture
Update app.py
296ab68 verified
Raw
History Blame Contribute Delete
5.52 kB
import pandas as pd
import numpy as np
from dotenv import load_dotenv
from langchain_community.document_loaders import TextLoader
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import CharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_huggingface import HuggingFaceEmbeddings
import gradio as gr
load_dotenv()
# Load and preprocess data
books = pd.read_csv("books_with_emotions.csv")
books["large_thumbnail"] = books["thumbnail"] + "&fife=w800"
books["large_thumbnail"] = np.where(
books["large_thumbnail"].isna(),
"not_found.jpg",
books["large_thumbnail"],
)
# Load document embeddings
raw_documents = TextLoader("tagged_description.txt", encoding="utf-8").load()
text_splitter = CharacterTextSplitter(separator="\n", chunk_size=0, chunk_overlap=0)
documents = text_splitter.split_documents(raw_documents)
embedding_model = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
db_books = Chroma.from_documents(documents, embedding_model)
# Recommendation logic
def retrieve_semantic_recommendations(query, category=None, tone=None, initial_top_k=100, final_top_k=32):
recs_with_scores = db_books.similarity_search_with_score(query, k=initial_top_k)
# Normalize scores to similarity percentages (lower distance = higher similarity)
max_score = max(score for _, score in recs_with_scores)
min_score = min(score for _, score in recs_with_scores)
normalized = [
(rec, 100 - ((score - min_score) / (max_score - min_score) * 100) if max_score > min_score else 100)
for rec, score in recs_with_scores
]
books_list = [
(int(rec.page_content.strip('"').split()[0]), similarity)
for rec, similarity in normalized
]
book_df = pd.DataFrame(books_list, columns=["isbn13", "similarity"])
book_recs = books.merge(book_df, on="isbn13").sort_values(by="similarity", ascending=False).head(initial_top_k)
if category != "All":
book_recs = book_recs[book_recs["simple_categories"] == category].head(final_top_k)
else:
book_recs = book_recs.head(final_top_k)
if tone == "Happy":
book_recs = book_recs[book_recs["joy"] > 0.2]
elif tone == "Surprising":
book_recs = book_recs[book_recs["surprise"] > 0.2]
elif tone == "Angry":
book_recs = book_recs[book_recs["anger"] > 0.2]
elif tone == "Suspenseful":
book_recs = book_recs[book_recs["fear"] > 0.2]
elif tone == "Sad":
book_recs = book_recs[book_recs["sadness"] > 0.2]
# Final sort by similarity to preserve top match order
book_recs = book_recs.sort_values(by="similarity", ascending=False)
return book_recs
# Gradio recommendation interface
def recommend_books(query, category, tone):
recommendations = retrieve_semantic_recommendations(query, category, tone)
results = []
for _, row in recommendations.iterrows():
description = row["description"]
truncated_desc_split = description.split()
truncated_description = " ".join(truncated_desc_split[:30]) + "..."
authors_split = row["authors"].split(";")
if len(authors_split) == 2:
authors_str = f"{authors_split[0]} and {authors_split[1]}"
elif len(authors_split) > 2:
authors_str = f"{', '.join(authors_split[:-1])}, and {authors_split[-1]}"
else:
authors_str = row["authors"]
similarity_score = f"πŸ”— {row['similarity']:.1f}% match"
caption = f"**{row['title']}** by _{authors_str}_\n\n{similarity_score}\n\n{truncated_description}"
results.append((row["large_thumbnail"], caption))
return results
categories = ["All"] + sorted(books["simple_categories"].dropna().unique())
tones = ["All", "Happy", "Surprising", "Angry", "Suspenseful", "Sad"]
# Gradio app with improved UX
with gr.Blocks(theme=gr.themes.Base(), css="""
#main-container { max-width: 1200px; margin: auto; }
.gallery-item img { border-radius: 12px; transition: transform 0.3s ease-in-out; }
.gallery-item:hover img { transform: scale(1.05); }
""") as dashboard:
with gr.Column(elem_id="main-container"):
gr.Markdown("# πŸ“š Semantic Book Recommender")
gr.Markdown("Describe a book you're in the mood for, and get personalized recommendations based on emotion and genre.")
with gr.Row():
user_query = gr.Textbox(
label="πŸ” What kind of book are you looking for?",
placeholder="e.g., A magical journey of friendship and discovery...",
lines=2,
)
with gr.Row():
category_dropdown = gr.Dropdown(
choices=categories,
label="πŸ“– Filter by genre:",
value="All"
)
tone_dropdown = gr.Dropdown(
choices=tones,
label="🎭 Desired emotional tone:",
value="All"
)
submit_button = gr.Button("✨ Recommend Books", size="lg")
gr.Markdown("## πŸ”Ž Top Matches")
output = gr.Gallery(
label="Recommended Books",
show_label=False,
columns=4,
object_fit="cover",
height="auto",
elem_classes=["gallery-item"]
)
submit_button.click(
fn=recommend_books,
inputs=[user_query, category_dropdown, tone_dropdown],
outputs=output
)
if __name__ == "__main__":
dashboard.launch()