import gradio as gr import numpy as np import os from huggingface_hub import login from sentence_transformers import SentenceTransformer, util import pandas as pd from datasets import load_dataset # --- 1. CONFIGURATION --- # Centralized place for all settings and constants. class Config: """Configuration settings for the application.""" EMBEDDING_MODEL_ID = "google/embeddinggemma-300M" PROMPT_NAME = "STS" TOP_K = 5 HF_TOKEN = os.getenv('HF_TOKEN') # --- 2. COLOR DATA --- # The color palette data is kept separate for clarity and easy modification. ds = load_dataset("burkelibbey/colors") df = pd.DataFrame(ds['train']) df = df.rename(columns={"color": "hex"}) # Split column into two new ones df[["name", "description"]] = df["description"].str.split(":", n=1, expand=True) df["name"] = df["name"].str.title() df = df.drop_duplicates(subset="name") COLOR_DATA = df.to_dict(orient="records") # --- 3. CORE LOGIC --- # Encapsulated in a class to manage state (model, embeddings) cleanly. class MoodPaletteGenerator: """Handles model loading, embedding generation, and palette creation.""" def __init__(self, config: Config, color_data: list[dict[str, any]]): """Initializes the generator, logs in, and loads necessary assets.""" self.config = config self.color_data = color_data self._login_to_hf() self.embedding_model = self._load_model() self.color_embeddings = self._precompute_color_embeddings() def _login_to_hf(self): """Logs into Hugging Face Hub if a token is provided.""" if self.config.HF_TOKEN: print("Logging into Hugging Face Hub...") login(token=self.config.HF_TOKEN) else: print("HF_TOKEN not found. Proceeding without login.") print("Note: This may fail if the model is gated.") def _load_model(self) -> SentenceTransformer: """Loads the Sentence Transformer model.""" print(f"Initializing embedding model: {self.config.EMBEDDING_MODEL_ID}...") try: return SentenceTransformer(self.config.EMBEDDING_MODEL_ID) except Exception as e: print(f"Error loading model: {e}") raise def _precompute_color_embeddings(self) -> np.ndarray: """Generates and stores embeddings for the color descriptions.""" print("Pre-computing embeddings for color palette...") color_texts = [ f"{color['name']}, {color['description']}" for color in self.color_data ] embeddings = self.embedding_model.encode( color_texts, prompt_name=self.config.PROMPT_NAME, show_progress_bar=True ) print("Embeddings computed successfully.") return embeddings def _get_text_color_for_bg(self, hex_color: str) -> str: """ Calculates the luminance of a hex color and returns black ('#000000') or white ('#FFFFFF') for the best text contrast. """ hex_color = hex_color.lstrip('#') try: r, g, b = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4)) luminance = (0.299 * r + 0.587 * g + 0.114 * b) return '#000000' if luminance > 150 else '#FFFFFF' except (ValueError, IndexError): return '#000000' # Default to black on invalid hex def _format_palette_as_html(self, top_hits: list[dict[str, any]]) -> str: """Formats the top color hits into a displayable HTML string.""" if not top_hits: return "
Could not generate a palette. Please try another mood.
" cards_html = "" for hit in top_hits: color_info = self.color_data[hit['corpus_id']] hex_code = color_info['hex'] name = color_info['name'] score = hit['score'] text_color = self._get_text_color_for_bg(hex_code) cards_html += f"""Please enter a mood or a description.
", "" mood_embedding = self.embedding_model.encode( mood_text, prompt_name=self.config.PROMPT_NAME ) top_hits = util.semantic_search( mood_embedding, self.color_embeddings, top_k=self.config.TOP_K )[0] palette_html = self._format_palette_as_html(top_hits) theme_css = self._create_dynamic_theme_css(top_hits) return palette_html, theme_css def clear_theme(self) -> tuple[str, str]: return "", "" # --- 4. GRADIO UI --- # Defines and launches the web interface. def create_ui(generator: MoodPaletteGenerator): """Creates the Gradio web interface.""" with gr.Blocks(theme=gr.themes.Soft()) as demo: # This invisible component will hold our dynamic CSS dynamic_css_output = gr.HTML() gr.Markdown(""" # 🎨 Mood Palette Generator Describe a mood, a scene, or a feeling, and get a matching color palette.