Spaces:
Running
Running
import os | |
import hashlib | |
import pandas as pd | |
from openai import OpenAI | |
import gradio as gr | |
input_file = "./data/sample_gpg_data.jsonl" | |
user_df = pd.read_json(input_file, lines=True) | |
user_ids = user_df["user_id"].unique().tolist() | |
client = OpenAI(api_key=os.environ.get('OPENAI_API_KEY')) | |
# Simple in-memory cache | |
guidance_cache = {} | |
profile_cache = {} | |
def hash_titles(titles): | |
joined = "\n".join(sorted(titles)) | |
return hashlib.md5(joined.encode("utf-8")).hexdigest() | |
def get_books(user_id): | |
if user_id is None: | |
return "Please select a user.", pd.DataFrame(), "" | |
user_info = user_df.loc[user_df["user_id"] == user_id] | |
print(user_info) | |
books_list = user_df.loc[user_df["user_id"] == user_id, "purchased_books"].values | |
if len(books_list) == 0: | |
return f"No books found for {user_id}.", pd.DataFrame(), "" | |
books = books_list[0] | |
df = pd.DataFrame(books) | |
df = df[['title', 'author', 'categories']].rename(columns={'title': 'Title', 'author': 'Author', 'categories': 'Category'}) | |
books_info = generate_books(books_list) | |
titles = [book["title"] for book in books if "title" in book] | |
cache_key = hash_titles(titles) | |
if cache_key in guidance_cache: | |
guidance_response = guidance_cache[cache_key] | |
profile_response = profile_cache[cache_key] | |
print("✅ Using cached response") | |
else: | |
print("🧠 Calling OpenAI API") | |
guidance_prompt_str = guidance_prompt(books_info) | |
guidance_response = client.chat.completions.create( | |
model="gpt-3.5-turbo", | |
messages=[{"role": "user", "content": guidance_prompt_str}], | |
temperature=0.3, | |
max_tokens=150 | |
).choices[0].message.content.strip() | |
guidance_cache[cache_key] = guidance_response | |
profile_response = client.chat.completions.create( | |
model="gpt-3.5-turbo", | |
messages=[ | |
{"role": "user", "content": profile_prompt(books_info, guidance_response)} | |
], | |
temperature=0.3, | |
max_tokens=150 | |
).choices[0].message.content.strip() | |
profile_cache[cache_key] = profile_response | |
candidates_options = list(user_info.get("candidate_options", [])) | |
rec_prompt = build_recommendation_prompt(profile_response, candidates_options) | |
choice = extract_choice(rec_prompt) | |
predicted_book = candidates_options[choice-1] if choice and 1 <= choice <= len(candidates_options) else None | |
target_book = user_info.get("target_asin", '') | |
print("target_book:", target_book) | |
return f"{user_id}", df, guidance_response, profile_response, rec_prompt, pd.DataFrame(candidates_options[0]), target_book.values, predicted_book[0]['asin'] | |
def extract_choice(response_text): | |
for token in response_text.split(): | |
if token.strip("[]").isdigit(): | |
return int(token.strip("[]")) | |
return None | |
def generate_books(books): | |
book_combos = [] | |
for book in books: | |
categories = ', '.join(book[0]['categories']) | |
book_combos.append(f"Title of the book is {book[0]['title']} and the category of the book is {categories}. Description of the book is {book[0]['description']}") | |
return book_combos | |
def guidance_prompt(titles): | |
return f"""Here is a list of books a person has read:\n{chr(10).join("- " + t for t in titles)}\n\nWhat genres or themes do you notice across these books? Please list them concisely.""" | |
def profile_prompt(titles, guidance): | |
return f"""Here is a list of books a person has read:\n{chr(10).join("- " + t for t in titles)}\n\nBased on the following genres/themes: {guidance}\n\nSummarize this person's book preferences in one paragraph.""" | |
def build_recommendation_prompt(profile, candidates): | |
prompt = f"""A user has the following reading preference:\n"{profile}"\n\nHere are some books they might consider next:\n""" | |
if len(candidates) == 1 and isinstance(candidates[0], list): | |
candidates = candidates[0] | |
for i, book in enumerate(candidates, start=1): | |
prompt += f"[{i}] {book.get('title', 'Unknown Title')}\n" | |
prompt += "\nWhich of these books best matches the user's preference? Respond ONLY with the number [1-4]." | |
return prompt | |
def get_books_theme(books): | |
return | |
with gr.Blocks() as demo: | |
gr.Markdown("## Select User") | |
user_dropdown = gr.Dropdown(choices=user_df["user_id"].tolist(), value=None, label="User ID") | |
gr.Markdown("## Selected User") | |
output_text = gr.Textbox(show_label=False) | |
gr.Markdown("## Books read") | |
output_table = gr.Dataframe(label="Books Read", interactive=False, show_label=False) | |
gr.Markdown("## User Books Theme") | |
output_theme = gr.Textbox(label="User Books Theme", lines=8, show_label=False) | |
gr.Markdown("## User Profile") | |
output_profile = gr.Textbox(label="User Profile", show_label=False, lines=6) | |
output_rec_prompt = gr.Textbox(label="Recommendation Prompt", lines=8) | |
output_candidate_options = gr.DataFrame(label="Candidate Books") | |
output_target_id = gr.Textbox(label="Target Book") | |
output_predicted_book = gr.Textbox(label="Predicted Book") | |
user_dropdown.change(fn=get_books, inputs=user_dropdown, outputs=[output_text, output_table, output_theme, output_profile, output_rec_prompt, output_candidate_options, output_target_id, output_predicted_book]) | |
if __name__ == "__main__": | |
demo.launch() | |