moul_lmemes / app.py
oumayma03's picture
Update app.py
bbfdb85 verified
raw
history blame
7.55 kB
CSV_PATH = "memes_descriptions.csv"
from pydantic import BaseModel, Field
from enum import Enum
import outlines
import openai
import pandas as pd
import spaces
from langchain.docstore.document import Document
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
import gradio as gr
from dotenv import load_dotenv
import os
load_dotenv()
class Meme(BaseModel):
link: str = Field(..., description="The URL of the meme")
description: str = Field(..., description="The description of the meme")
# --- CONFIG ---
MODEL_NAME = "qwen/qwen3-32b:free"
CSV_PATH = "memes_descriptions.csv" # Updated CSV_PATH to reflect the data directory
API_KEY=os.getenv("OPENROUTER_API_KEY")
try:
client = openai.OpenAI(base_url="https://openrouter.ai/api/v1", api_key=API_KEY)
model = outlines.from_openai(client, MODEL_NAME)
except Exception as e:
print(f"❌ Failed to initialize OpenRouter client: {e}")
client = None
# --- LOAD DATA ---
def load_data_and_create_vectorstore():
global documents, vectorstore, retriever
try:
df = pd.read_csv(CSV_PATH).fillna({"description": "", "link": ""})
df.columns = df.columns.str.strip().str.lower() # Normalize header names
documents = [
Document(
page_content=row["description"],
metadata={"url": str(row["link"]).strip()}
)
for _, row in df.iterrows()
]
embedding_model = HuggingFaceEmbeddings(model_name="intfloat/multilingual-e5-large-instruct")
vectorstore = FAISS.from_documents(documents, embedding_model)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
print("βœ… Data loaded and vectorstore created.")
except FileNotFoundError:
print(f"❌ Data file not found at {CSV_PATH}")
documents, vectorstore, retriever = [], None, None
except Exception as e:
print(f"❌ Error loading data or creating vectorstore: {e}")
documents, vectorstore, retriever = [], None, None
load_data_and_create_vectorstore()
# --- LLM ---
def ask_llm(question: str, docs: list) -> Meme:
if client is None:
return "❌ LLM client not initialized."
context = "\n\n".join(
f"Meme {i+1}:\nDescription: {doc.page_content}\nLink: {doc.metadata.get('url', 'N/A')}"
for i, doc in enumerate(docs)
)
messages = f"""You're a meme expert. The user will ask something, and your job is to select the most relevant meme from the following, keep the url link as it is :\n{context} \n\n === example generation === \n link='https://drive.google.com/file/d/1DrXjMZDn-fd7mJDKxZpIbYq1zuatLAQ2/view?usp=drive_link' description="This meme is perhaps the most relevant to situations where someone is trying to distance themselves from trouble, responsibility or confusion. The innocent defense, along with the exaggerated seriousness of the context, captures this human emotion perfectly. This clip is particularly versatile, since it can be used for everything from dodging blame in a work environment to the cartoonish helplessness of dealing with everyday chaos. The child's exclamation is a concise and universally understood expression of disassociation, making it an ideal visual accompaniment to anything from minor mishaps to adventurous tales of escape. It's a strong contender for embodying the act of avoiding situations with humor. The rooting for the 'underdog' (or in this case, the underkid) provides both light-hearted commentary on life's inevitable awkward moments and a shared sense of solidarity in the face of confusion."
\n\n User : {question}"""
print(messages)
try:
meme = model(messages,Meme, stream=False)
print(meme)
meme = Meme.model_validate_json(meme)
print(meme)
return meme
except Exception as e:
return f"❌ LLM Error: {e}"
# --- MAIN QUERY ---
@spaces.GPU(duration=90)
def query_memes(user_input: str):
if retriever is None:
return "❌ RAG system not initialized due to errors.", ""
src_docs = retriever.invoke(user_input)
meme = ask_llm(user_input, src_docs)
raw_url = meme.link
description = meme.description
file_id = raw_url.split("/file/d/")[1].split("/")[0]
embed_url = f"https://drive.google.com/file/d/{file_id}/preview"
embed_html = f'''
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; border-radius: 8px; box-shadow: 0 4px 10px rgba(0,0,0,0.2); margin-top: 1em;">
<iframe src="{embed_url}"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"
frameborder="0" allow="autoplay" allowfullscreen>
</iframe>
</div>
'''
return embed_html, description
# --- UPLOAD FUNCTION ---
def upload_meme(video_file, description):
if video_file is None or description is None:
return "Please provide both a video file and a description."
try:
# This is a placeholder. In a real application, you would upload the video
# to a hosting service (like Google Drive, S3, etc.) and get a shareable link.
# For this example, we'll just create a dummy link.
# You might need to implement actual file upload logic here.
dummy_link = f"https://dummy-hosting.com/videos/{os.path.basename(video_file.name)}"
new_meme = pd.DataFrame({"link": [dummy_link], "description": [description]})
# Append to the CSV file
new_meme.to_csv(CSV_PATH, mode='a', header=False, index=False)
# Reload data and update vectorstore
load_data_and_create_vectorstore()
return "Meme uploaded successfully!"
except Exception as e:
return f"Error uploading meme: {e}"
# --- GRADIO INTERFACE ---
if __name__ == "__main__":
if retriever is None or client is None:
print("Gradio interface will not run due to RAG/LLM initialization errors.")
else:
with gr.Blocks(title="Moul Lmemes 🎬") as demo:
gr.Markdown("# Moul Lmemes 🎬")
gr.Markdown("Write what you are looking for and i will find the most revelant moroccan meme for you πŸ”")
with gr.Tab("Search Memes"):
search_input = gr.Textbox(label="Type something")
search_button = gr.Button("Find Meme")
search_output_video = gr.HTML(label="Top Meme Video")
search_output_text = gr.Textbox(label="Results")
search_button.click(
fn=query_memes,
inputs=search_input,
outputs=[search_output_video, search_output_text]
)
gr.Examples(
examples=[["a man running"],["sharing"],["immigrant in france"]],
inputs=search_input,
outputs=[search_output_video, search_output_text],
fn=query_memes,
cache_examples=True,
)
with gr.Tab("Upload Meme"):
upload_video = gr.Video(label="Upload Video")
upload_description = gr.Textbox(label="Description")
upload_button = gr.Button("Upload Meme")
upload_output = gr.Textbox(label="Upload Status")
upload_button.click(
fn=upload_meme,
inputs=[upload_video, upload_description],
outputs=upload_output
)
demo.launch(debug=True)