LyricsChatBot / app.py
Rob Learsch
Update app.py
0a2cd85
import gradio as gr
from huggingface_hub import InferenceClient
#import base64
import os
#from google import genai
#from google.genai import types
from sentence_transformers import SentenceTransformer
from dotenv import load_dotenv
import numpy as np
import random
from PIL import Image
import io
load_dotenv()
"""
For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
"""
def return_image(artist):
if artist == "Radiohead":
return random.choice(["radiohead.png", "radiohead2.png",])
if artist == "Kendrick Lamar":
return random.choice(['kendrick3.png','kendrick3.png',"kendrick2.png", "kendrick4.png"])
if artist == "Grateful Dead":
return random.choice(["bob.png", "bob2.png", "jerry.png"]) # Randomly select between bob.png and bob2.png
if artist == "Google Gemma":
return "gemma.png"
return "radiohead.png"
#def find_most_relevant_lyric(lyrics, user_input):
# user_doc = nlp(user_input)
# best_match = max(lyrics, key=lambda lyric: user_doc.similarity(nlp(lyric)))
# return best_match
#
#def stitch_lyrics(lyrics, line_number=1):
# return [lyrics[i] + " " + lyrics[i + line_number] for i in range(len(lyrics) - line_number)]
# Load lyrics from a text file
def load_lyrics(filename):
with open(filename, "r", encoding="utf-8") as file:
lyrics = file.readlines()
return [line for line in lyrics]
#return [line for line in lyrics]
def songs_from_text(lines):
songs = []
current_song = []
current_stanza = []
for line in lines:
line = line.strip()
if line == "==================================================": # New song delimiter
if current_stanza:
current_song.append(current_stanza)
current_stanza = []
if current_song:
songs.append(current_song)
current_song = []
continue
if line == "": # New stanza delimiter
if current_stanza:
current_song.append(current_stanza)
current_stanza = []
continue
current_stanza.append(line)
if current_stanza:
current_song.append(current_stanza)
if current_song:
songs.append(current_song)
return songs
def generate_cumulative_phrases(songs):
all_phrases = []
for song in songs:
for stanza in song:
for i in range(len(stanza)):
cumulative = ""
for j in range(i, min(len(stanza),4)):
cumulative += (" // " if cumulative else "") + stanza[j]
all_phrases.append(cumulative)
return all_phrases
def artist_response(gemma_response, artist):
if artist == "Radiohead":
artist_embeddings = radiohead_embeddings
lyric_list = all_phrases_radiohead
#lyric_list = stitched_radiohead_lyrics
if artist == "Kendrick Lamar":
artist_embeddings = kendrick_embeddings
lyric_list = all_phrases_kendrick
if artist == "Grateful Dead":
artist_embeddings = grateful_dead_embeddings
lyric_list = all_phrases_grateful_dead
if artist == "Google Gemma":
return gemma_response
encoder = get_encoder()
encoded_gemma = encoder.encode(gemma_response, precision="int8")
#encoded_gemma = encoder_model.encode(gemma_response)
similarity_result = cosine_similarity_int8(encoded_gemma, artist_embeddings)
result_max_index = np.argmax(similarity_result)
lyric_response = lyric_list[result_max_index]
return lyric_response
def chat_with_musician(user_input, history, artist):
global artist_history
if history is None:
history = []
previous_artist = artist_history[-1]
if artist != previous_artist:
history.clear()
# Convert Gradio history tuples to HF message dicts
messages = []
for user_msg, bot_msg in history[-5:]: # last 5 exchanges
messages.append({"role": "user", "content": user_msg})
messages.append({"role": "assistant", "content": bot_msg})
# Add current user message
messages.append({"role": "user", "content": system_message + "\n\n" + user_input})
try:
response = client.chat.completions.create(
model="zai-org/GLM-4.7-Flash",
messages=messages,
max_tokens=256,
temperature=0.75,
)
gemma_response = response.choices[0].message.content
except Exception as e:
gemma_response = f"Error: {str(e)}"
lyric_response = artist_response(gemma_response, artist)
# Check for repeated response logic (optional)
if len(messages) > 1 and lyric_response == messages[-2]["content"]:
messages[-1] = {"role": "user", "content": system_message_repeated + "\n\n" + user_input}
try:
response = client.chat.completions.create(
model="zai-org/GLM-4.7-Flash",
messages=messages,
max_tokens=256,
temperature=0.75,
)
gemma_response = response.choices[0].message.content
except Exception as e:
gemma_response = f"Error: {str(e)}"
lyric_response = artist_response(gemma_response, artist)
# Append new exchange to Gradio history format
history.append((user_input, lyric_response))
artist_history.append(artist)
artist_history[:] = artist_history[-10:]
return lyric_response
def cosine_similarity_int8(query, embeddings):
# query: (d,)
# embeddings: (n, d)
query = query.astype(np.int32)
embeddings = embeddings.astype(np.int32)
dots = embeddings @ query
query_norm = np.linalg.norm(query)
emb_norms = np.linalg.norm(embeddings, axis=1)
return dots / (emb_norms * query_norm + 1e-8)
HF_API_KEY = os.environ["HF_API_KEY"]
_encoder_model = None
def get_encoder():
global _encoder_model
if _encoder_model is None:
_encoder_model = SentenceTransformer('all-MiniLM-L6-v2',
#'sentence-transformers/all-MiniLM-L6-v2',
#backend='openvino',
#model_kwargs={"file_name": "openvino/openvino_model_qint8_quantized.xml"},
#to increase speed:
#similarity_function=SimilarityFunction.DOT_PRODUCT,
)
return _encoder_model
radiohead_embeddings = np.load("radiohead_embeddings.npy", mmap_mode="r")
kendrick_embeddings = np.load("kendrick_embeddings.npy", mmap_mode="r")
grateful_dead_embeddings = np.load("grateful_dead_embeddings.npy", mmap_mode="r")
radiohead_lyrics = load_lyrics("radiohead_lyrics.txt")
kendrick_lyrics = load_lyrics("kendrick_lamar_lyrics.txt")
grateful_dead_lyrics = load_lyrics('grateful_dead_lyrics.txt')
all_phrases_radiohead = generate_cumulative_phrases(songs_from_text(radiohead_lyrics))
all_phrases_kendrick = generate_cumulative_phrases(songs_from_text(kendrick_lyrics))
all_phrases_grateful_dead = generate_cumulative_phrases(songs_from_text(grateful_dead_lyrics))
size = 350 #256
# Initialize Hugging Face Inference Client
client = InferenceClient(token=HF_API_KEY,
#model="MiniMaxAI/MiniMax-M2.1",)
)
system_message = "Don't be too repetitive. Please limit your response to only a few sentences."
artist_history = [""] # If you want to track previous artist selection
# Size for the image thumbnail (set your size)
size = 350 #150
def respond(message, artist, chat_history):
if not message:
return chat_history
reply = f"Echo ({artist}): {message}"
chat_history = chat_history or []
chat_history.append((message, reply))
return chat_history
def chatbot_response(message, artist, chat_history):
global artist_history
if message is None or message.strip() == "":
return chat_history or []
response = chat_with_musician(message, chat_history or [], artist)
chat_history = chat_history or []
chat_history.append((message, response))
return chat_history
def update_artist_image(artist):
# Call your existing function to get the image path or PIL.Image
return return_image(artist)
with gr.Blocks() as demo:
with gr.Row():
with gr.Column(scale=1):
artist_dropdown = gr.Dropdown(
choices=["Radiohead", "Kendrick Lamar", "Grateful Dead", "Google Gemma"],
value="Radiohead",
label="Select artist",
interactive=True,
)
artist_image = gr.Image(
value=return_image("Radiohead"),
label="Thumbnail",
height=size,
width=size,
show_label=False,
show_fullscreen_button=False,
show_download_button=False,
show_share_button=False,
)
with gr.Column(scale=1):
chatbot = gr.Chatbot(height=400, type='messages')
message_input = gr.Textbox(
label="Your message",
placeholder="Enter a message and press Enter",
lines=2,
interactive=True,
)
artist_dropdown.change(fn=update_artist_image, inputs=artist_dropdown, outputs=artist_image)
message_input.submit(fn=chatbot_response, inputs=[message_input, artist_dropdown, chatbot], outputs=chatbot).then(lambda: "", None, message_input)
if __name__ == "__main__":
demo.launch()